Cloud computing isn't just about application deployment. There are also a growing number of cloud-based web services that you can use to develop your application. One of the most well known is Amazon's Simple Storage Service. But there are many others including web services for messaging, relational and NoSQL databases, email and telephony. Using these services allows you to build highly scalable applications without the pain and cost of having to develop and operate your own infrastructure. In this presentation, you will learn about the benefits and drawbacks of these Web services; their typical use cases and how to use them. We will describe a location aware, telephony application that is built using cloud services. You will learn about strategies for building resilient, fault tolerant applications that consume cloud services.
JFokus: Cubes, Hexagons, Triangles, and More: Understanding Microservices
Developing applications with Cloud Services (jax jax2013)
1. DEVELOPING WITH CLOUD
SERVICES
Chris Richardson
Author of POJOs in Action
Founder of the original CloudFoundry.com
@crichardson
chris.richardson@springsource.com
http://plainoldobjects.com
21. @crichardson
Use cloud-based services
• Highly scalable services
• Someone else’s headache to develop and maintain
• Provided by IaaS/PaaS
• Provided by 3rd party
23. @crichardson
Thousands of 3rd party services
http://www.programmableweb.com/apis/directory
http://www.slideshare.net/jmusser/j-musser-apishotnotgluecon2012
24. @crichardson
• Predominantly REST
• Predominantly JSON
• > billion API calls/day:Twitter, Google, Facebook, Netflix,
Accuweather, ...
• Increasing number of API-only companies
http://www.slideshare.net/jmusser/j-musser-apishotnotgluecon2012
Cloud service trends
26. @crichardson
Benefits of cloud services
• Someone else’s headache to develop and operate
• Focus on your core business problem
• Get up and running quickly
• Elasticity
• Capex Opex
27. @crichardson
Drawbacks of cloud services
• Complexity and drawbacks of a distributed system
• You are dependent on service provider
28. @crichardson
Risks of cloud services
Urban Airship’s Strategic Partnership With
SimpleGeoTurns Into An Acquisition
42. @crichardson
Spring Data for MongoDB
• Provides MongoTemplate
• Analogous to JdbcTemplate
• Hides boilerplate code
• Domain object Document mapping
43. @crichardson
Using Spring data: creating an
index on location attribute
@Component
class MongoFriendService extends FriendService {
@Autowired
var mongoTemplate: MongoTemplate = _
@PostConstruct
def createGeoIndex {
val dbo = new BasicDBObject
dbo.put("location", "2d")
mongoTemplate.getCollection("friendRecord").ensureIndex(dbo)
}
Create geospatial 2d index
Collection name
Attribute and
index type
44. @crichardson
Using Spring Data: adding record
@Component
class MongoFriendService extends FriendService {
override def addOrUpdate(request: AddOrUpdateUserRequest) = {
val name = request.name
val phoneNumber = request.phoneNumber
val fr = new FriendRecord(phoneNumber, name,
new Point(request.longitude, request.latitude))
mongoTemplate.save(fr)
}
case class FriendRecord(id : String,
name : String,
location : Point)
45. @crichardson
Using Spring Data: finding nearby
friends
@Component
class MongoFriendService extends FriendService {
override def findNearbyFriends(request: NearbyFriendsRequest) = {
val location = new Point(request.longitude, request.latitude)
val distance = new Distance(3, Metrics.MILES)
val query = NearQuery.near(location).maxDistance(distance)
val result = mongoTemplate.geoNear(query, classOf[FriendRecord])
val nearby = result.getContent.map(_.getContent)
FindNearbyFriendsResponse(nearby.map(f => FriendInfo(f.name, f.id)))
}
47. @crichardson
$ vmc push vme-user --path web/target/
Application Deployed URL [cer-spring.cloudfoundry.com]:
Detected a Java SpringSource Spring Application, is this correct? [Yn]:
Memory Reservation (64M, 128M, 256M, 512M, 1G) [512M]:
Creating Application: OK
Would you like to bind any services to 'vme-user'? [yN]: y
Would you like to use an existing provisioned service? [yN]: y
The following provisioned services are available
1: vme-mongo
2: mysql-135e0
Please select one you wish to use: 1
Binding Service [vme-mongo]: OK
Uploading Application:
Checking for available resources: OK
Processing resources: OK
Packing application: OK
Uploading (12K): OK
Push Status: OK
Binding a service to an application
Would you like to bind any services to 'vme-user'? [yN]: y
Would you like to use an existing provisioned service? [yN]: y
The following provisioned services are available
1: vme-mongo
2: mysql-135e0
Please select one you wish to use: 1
Binding Service [vme-mongo]: OK
52. @crichardson
Restaurant Service
@Service
class FactualRestaurantService extends RestaurantService {
@Value("${factual_consumer_key}") var consumerKey: String = _
@Value("${factual_consumer_secret}") var consumerSecret: String = _
var factual: Factual = _
@PostConstruct
def initialize {
factual = new Factual(consumerKey, consumerSecret, true)
}
override def findNearbyRestaurants(location: Location) = {
...
val restaurants = factual.get.fetch("restaurants-us",
new Query().within(new Circle(location.lat, location.lon, 1000)).limit(5))
val rs = restaurants.getData.map { map =>
RestaurantInfo(map.get("name").asInstanceOf[String])
}
FindNearbyRestaurantResponse(rs.toList)
}
...
5 restaurants within 1km
53. @crichardson
Agenda
• Why use cloud services?
• Developing location-based applications
• Building SMS and telephony enabled applications
• Developing robust, fault tolerant applications
54. @crichardson
The telephony and SMS are
important
http://blog.nielsen.com/nielsenwire/online_mobile/new-mobile-obsession-
u-s-teens-triple-data-usage/
7/ waking hr !
Nielsen
57. @crichardson
VoteMeetEat.com &Telephony
• Handling registration SMS
• Sending SMS notifying users to vote
• Handling incoming voice call from voters:
• Text-to-speech of restaurants options
• Collecting digits entered via keypad
• Sending SMS/Voice notifications of voting results
58. @crichardson
DIY telephony = Difficult
• Difficult to setup and operate
• Expensive
• Complex SMS protocols
• …
Better to use SMS/Telephony-aaS:
59. @crichardson
Telephony/SMS - aaS
• SMS
• Inbound and outgoing calls
• Recording and transcription
• SMS
• Inbound and outgoing calls
• Recording and transcription
• Twitter
• IM
60. @crichardson
Twilio -Telephony and SMS as a
service
• REST API
• Allocate phone numbers
• Make and receive phone calls
• Send and receive SMS messages
• Pay per use:
• Phone calls - per-minute
• SMS – per SMS sent or received
• Phone number – per month
• Examples
• OpenVBX is a web-based, open source phone system
• StubHub – notifies sellers of a pending sale via phone
• SurveyMonkey – interactive polling
• Salesforce – SMS-based voting for 19,000 conference attendees
66. @crichardson
Inviting users to vote
POST /2010-04-01/Accounts/≪AccountSID≫/SMS/Messages
From=+15105551212
&To=+14155551212
&Body=≪MESSAGE≫
Authorization: Basic ....
Basic auth usingTwilio
AccountSid+AuthToken
67. @crichardson
Sending SMS using the Spring
RestTemplate
@Component
class TwilioService {
def sendSms(recipient : String, message : String) = {
val response = postToTwilio("SMS/Messages",
Map("From" -> twilioPhoneNumber, "To" -> recipient, "Body" -> message))
(response "SMSMessage" "Sid").text
}
68. @crichardson
Sending SMS using the Spring
RestTemplate
@Component
class TwilioService {
def postToTwilio(resourcePath : String, requestParams : Map[String, String]) = {
val entity = makeEntity(requestParams)
try {
val response = restTemplate.postForObject(twilioUrl +
"/Accounts/{accountSid}/{resource}",
entity, classOf[String],
accountSid, resourcePath)
XML.loadString(response)
} catch {
case e : HttpClientErrorException if e.getStatusCode == HttpStatus.BAD_REQUEST =>
val body = e.getResponseBodyAsString()
val xmlBody = XML.loadString(body)
val code = Integer.parseInt((xmlBody "Code").text)
val message = (xmlBody "Message").text
throw new TwilioRestException(message, code)
}
}
70. @crichardson
Voting
Twilio
Survey Management
<Response>
<Say> Chris would like to meet and eat. </Say>
<Gather action="handleresponse.html"
method="POST" numDigits="1">
<Say>Press 1 for ....</Say>
<Say>Press 2 for ....</Say>
</Gather>
</Response>
HTTP POST
http://≪voiceUrl≫?From=≪PhoneNumber≫
Call
71. @crichardson
Voting
Twilio
Survey Management
<Response>
<Say>Thank you for choosing.
The most popular place so far is ...
</Say>
<Pause/>
<Say>You will hear from us soon. Good bye</Say>
<Hangup/>
</Response>
HTTP POST
http://....handleresponse.html?
From=≪PhoneNumber≫&Digits=≪...≫
Digits
72. @crichardson
Voting code 1
@Controller
class TwilioController {
@Autowired
var surveyManagementService: SurveyManagementService = _
@RequestMapping(value = Array("/begincall.html"))
@ResponseBody
def beginCall(@RequestParam("From") callerId: String) = {
surveyManagementService.findSurveyByCallerId(callerId) match {
case None =>
<Response>
<Say>Sorry don't recognize your number</Say>
<Hangup/>
</Response>
case Some(survey) =>
<Response>
<Say>{ survey.prompt }</Say>
<Gather action="handleresponse.html" method="POST" numDigits="1">
{
for ((choice, index) <- survey.choices zipWithIndex)
yield <Say>Press { index } for { choice }</Say>
}
</Gather>
<Say>We are sorry you could not decide</Say>
<Hangup/>
</Response>
}
}
73. @crichardson
Voting code 2
class TwilioController {
...
@RequestMapping(value = Array("/handleresponse.html"))
@ResponseBody
def handleUserResponse(@RequestParam("From") callerId: String,
@RequestParam("Digits") digits: Int) = {
val survey = surveyManagementService.recordVote(callerId, digits)
<Response>
<Say>Thank you for choosing. The most popular place so far is
{ survey.map(_.mostPopularChoice) getOrElse "oops" }
</Say>
<Pause/>
<Say>You will hear from us soon. Good bye</Say>
<Hangup/>
</Response>
}
}
74. @crichardson
Agenda
• Why use cloud services?
• Developing location-based applications
• Building SMS and telephony enabled applications
• Developing robust, fault tolerant applications
75. @crichardson
The need for parallelism
Service A
Service B
Service C
Service D
b = serviceB()
c = serviceC()
d = serviceD(b, c)
Call in parallel
76. @crichardson
Futures are a great concurrency
abstraction
• Object that will contain the result of a concurrent computation -
http://en.wikipedia.org/wiki/Futures_and_promises
• Various implementations
• Java 7 Futures = ok
• Guava ListenableFutures = better
• Scala’s composable Futures = really good
• Java 8 CompletableFuture = great
Future<Integer> result =
executorService.submit(new Callable<Integer>() {... });
77. @crichardson
Using futures to parallelize requests
trait FriendService {
def findNearbyFriends(request : NearbyFriendsRequest) :
Future[FindNearbyFriendsResponse]
}
trait RestaurantService {
def findNearbyRestaurants(location: Location) :
Future[FindNearbyRestaurantResponse]
}
val f1 = friendsService.findNearbyFriends(NearbyFriendsRequest.fromLocation(vmeRecord.location))
val f2 = restaurantService.findNearbyRestaurants(vmeRecord.location)
val nearbyFriends = f1.get(2, TimeUnit.SECONDS)
val nearbyRestaurants = f2.get(2, TimeUnit.SECONDS)
Two calls execute concurrently
Client
Side
Proxies
82. @crichardson
How to run out of threads
Tomcat
Execute thread
pool
HTTP Request
Thread 1
Thread 2
Thread 3
Thread n
Service A Service B
If service B is down
then thread will be
blocked
XX
X
X
X
Eventually all threads
will be blocked
83. @crichardson
Their approach
• Network timeouts and retries
• Invoke remote services via a bounded thread pool
• Use the Circuit Breaker pattern
• On failure:
• return default/cached data
• return error to caller
• https://github.com/Netflix/Hystrix
http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html
84. @crichardson
@Service
class FactualRestaurantService extends RestaurantService {
@Autowired
@Qualifier("factualHystrixConfig")
var factualHystrixConfig: HystrixCommand.Setter = _
override def findNearbyRestaurants(location: Location) = {
class FindRestaurantsCommand
extends HystrixCommand[FindNearbyRestaurantResponse]
(factualHystrixConfig
.andCommandKey(HystrixCommandKey.Factory.asKey("FindRestaurantsCommand"))) {
override def run() = {
val restaurants = factual.fetch("restaurants",
new Query().within(new Circle(location.lat, location.lon, 1000)).limit(5))
val rs = for (map <- restaurants.getData) yield {
RestaurantInfo(map.get("name").asInstanceOf[String])
}
FindNearbyRestaurantResponse(rs.toList)
}
}
new FindRestaurantsCommand().queue()
}
}
Using Hystrix
85. @crichardson
Summary
Cloud services are highly scalable services developed and
operated by a 3rd party
Let’s you focus on your core business problem
Risk: provider is acquired and stops offering service
Developing an application that reliably consumes cloud
services requires careful design