Transcript
Page 1: Developing applications with Cloud Services  #javaone 2012

DEVELOPING WITH CLOUD SERVICESChris Richardson

Author of POJOs in ActionFounder of the original CloudFoundry.com

@[email protected]://plainoldobjects.com/

Page 2: Developing applications with Cloud Services  #javaone 2012

Presentation goal

How to build robust, scalable applications with

Cloud Services

Page 3: Developing applications with Cloud Services  #javaone 2012

About Chris

Page 4: Developing applications with Cloud Services  #javaone 2012

(About Chris)

Page 5: Developing applications with Cloud Services  #javaone 2012

About Chris()

Page 6: Developing applications with Cloud Services  #javaone 2012

About Chris

Page 7: Developing applications with Cloud Services  #javaone 2012

About Chris

http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/

Page 8: Developing applications with Cloud Services  #javaone 2012

vmc push About-Chris

Developer Advocate for CloudFoundry.com

Signup at http://cloudfoundry.compromo code: cfjavaone

Page 9: Developing applications with Cloud Services  #javaone 2012

Agenda

• Why use cloud services?

• Developing location-based applications

• Building SMS and telephony enabled applications

• Developing robust, fault tolerant applications

Page 10: Developing applications with Cloud Services  #javaone 2012

Three phases of every galactic civilization

Survival

Inquiry

Sophistication

Page 11: Developing applications with Cloud Services  #javaone 2012

Three phases of every galactic civilization

How

Why

Where

can we eat?

do we eat?

Where shall we have lunch?

Page 12: Developing applications with Cloud Services  #javaone 2012

Where shall we have lunch?

Solved by VoteMeetEat.com

Page 13: Developing applications with Cloud Services  #javaone 2012

VoteMeetEat.com

•What restaurants are nearby?•Which friends are close by? •Where do your friends prefer to eat?

To sign up text "register" to 510-555-????

Page 14: Developing applications with Cloud Services  #javaone 2012

VoteMeetEat.com

Restaurant database +

SMS +

Voice callsTo sign up text

"register" to 510-555-????

Page 15: Developing applications with Cloud Services  #javaone 2012

Key story: registration

5551212

Page 16: Developing applications with Cloud Services  #javaone 2012

Key story: registration

+5105551212

Page 17: Developing applications with Cloud Services  #javaone 2012

Key story: voting

555 1212

Page 18: Developing applications with Cloud Services  #javaone 2012

Key story: announce location

Page 19: Developing applications with Cloud Services  #javaone 2012

VOTEMEETEAT.COM

To sign up text "register" to 510-555-????

Page 20: Developing applications with Cloud Services  #javaone 2012

High-level architecture

VoteMeetEat

TelephonyIntegration

Friend GeoDatabase

Restaurant Database

MobilePhone

Do we really want to build all this?

DIY = DIFFICULT

Page 21: Developing applications with Cloud Services  #javaone 2012

Use cloud-based services

• Highly scalable services

• Someone else’s headache to develop and maintain

• Provided by IaaS/PaaS

• Provided by 3rd party

Page 22: Developing applications with Cloud Services  #javaone 2012

Cloud Foundry services

Page 23: Developing applications with Cloud Services  #javaone 2012

Thousands of 3rd party services

http://www.programmableweb.com/apis/directory

http://www.slideshare.net/jmusser/j-musser-apishotnotgluecon2012

Page 24: Developing applications with Cloud Services  #javaone 2012

• 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

Page 25: Developing applications with Cloud Services  #javaone 2012

Diverse

Page 26: Developing applications with Cloud Services  #javaone 2012

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

Page 27: Developing applications with Cloud Services  #javaone 2012

Drawbacks of cloud services

• Complexity and drawbacks of a distributed system

• You are dependent on service provider

Page 28: Developing applications with Cloud Services  #javaone 2012

Risks of cloud services

Urban Airship’s Strategic Partnership With SimpleGeo Turns Into An Acquisition

Page 29: Developing applications with Cloud Services  #javaone 2012

Cloud Services-based architecture

VoteMeetEat

TwilioMongoDB

Factual.Com

MobilePhone

Page 30: Developing applications with Cloud Services  #javaone 2012

DEMO

Page 31: Developing applications with Cloud Services  #javaone 2012

Agenda

• Why use cloud services?

• Developing location-based applications

• Building SMS and telephony enabled applications

• Developing robust, fault tolerant applications

Page 32: Developing applications with Cloud Services  #javaone 2012

Location-based services are hot!

Page 33: Developing applications with Cloud Services  #javaone 2012
Page 34: Developing applications with Cloud Services  #javaone 2012

Client-side APIs for finding location

W3C Geolocation API

Page 35: Developing applications with Cloud Services  #javaone 2012

BUT what about the server-side?

Page 36: Developing applications with Cloud Services  #javaone 2012

Lots of really difficult problems

•Scalable, spatial database – CRUD records, find nearby•Data management – database of places, street information•Forward geo-coding: address ⇒ lat/lon

•Reverse geo-coding: lat/lon ⇒ address

•Maps•Directions

Easier to use Geo-aaS

Page 37: Developing applications with Cloud Services  #javaone 2012

Examples of Geo-aaS

Beware the terms of service

• Maps• Forward and reverse geocoding• Directions• Elevation• Places

• Freely available geographic database

• Various APIs including reverse geocoding

• Business+review database• Neighborhood database

• Places database• Reverse geocoding

Page 38: Developing applications with Cloud Services  #javaone 2012

VOTEMEETEAT & Geo

trait FriendService { def addOrUpdate(request : AddOrUpdateUserRequest) def findNearbyFriends(request : NearbyFriendsRequest) :

FindNearbyFriendsResponse}

trait RestaurantService { def findNearbyRestaurants(location: Location) :

FindNearbyRestaurantResponse}

Page 39: Developing applications with Cloud Services  #javaone 2012

Implementing the friends database

Page 40: Developing applications with Cloud Services  #javaone 2012

MongoDB

• Document-oriented database

• Very fast, highly scalable and available

• Rich query language that supports location-based queries

• Provided by CloudFoundry.com

Page 41: Developing applications with Cloud Services  #javaone 2012

MongoDB server

Database: VoteMeetEatCollection: friendRecord

Storing friends in MongoDB

{ "_id": "+15105551212", "name": "Chris R.", "location": { "x": -122.25206103187264, "y": 37.847427441773796 }}

Page 42: Developing applications with Cloud Services  #javaone 2012

Spring Data for MongoDB

• Provides MongoTemplate

• Analogous to JdbcTemplate

• Hides boilerplate code

• Domain object ↔ Document mapping

Page 43: Developing applications with Cloud Services  #javaone 2012

Using Spring data: creating an index on location attribute

@Componentclass 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

Page 44: Developing applications with Cloud Services  #javaone 2012

Using Spring Data: adding record@Componentclass 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)

Page 45: Developing applications with Cloud Services  #javaone 2012

Using Spring Data: finding nearby friends

@Componentclass 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))) }

Page 46: Developing applications with Cloud Services  #javaone 2012

MongoDB and Cloud Foundry

$ vmc create-service mongodb vme-mongo

Page 47: Developing applications with Cloud Services  #javaone 2012

$ 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: OKWould you like to bind any services to 'vme-user'? [yN]: y

Would you like to use an existing provisioned service? [yN]: yThe following provisioned services are available

1: vme-mongo2: mysql-135e0Please select one you wish to use: 1

Binding Service [vme-mongo]: OKUploading 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]: yWould you like to use an existing provisioned service? [yN]: yThe following provisioned services are available1: vme-mongo2: mysql-135e0Please select one you wish to use: 1Binding Service [vme-mongo]: OK

Page 48: Developing applications with Cloud Services  #javaone 2012

Connecting to MongoDB <bean id="mongoTemplate"

class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg ref="mongoFactory" /> </bean>

<beans profile="default"> <mongo:db-factory id="mongoFactory" dbname="surveygeo" /> </beans>

<beans profile="cloud"> <cloud:mongo-db-factory id="mongoFactory" /> </beans>

Outside of Cloud Foundry

Inside Cloud Foundry

Page 49: Developing applications with Cloud Services  #javaone 2012

Implementing the restaurant database

Page 50: Developing applications with Cloud Services  #javaone 2012

Using Factual

• Geographic database as a Service

• Including 800,000 restaurants in the US

• Pricing: 10K calls day free, pay per use

Page 51: Developing applications with Cloud Services  #javaone 2012

Factual API

• RESTful/JSON interface

• Uses 2-legged OAuth 1.0.

• Geo and text filters

• Pagination

• Libraries for various languages

Page 52: Developing applications with Cloud Services  #javaone 2012

Restaurant Service@Serviceclass FactualRestaurantService extends RestaurantService {

@Value("${factual_consumer_key}") var consumerKey: String = _ @Value("${factual_consumer_secret}") var consumerSecret: String = _

var factual: ThreadLocal[Factual] = _

@PostConstruct def initialize { factual = new ThreadLocal[Factual] { override def initialValue() = 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 = for (map <- restaurants.getData) yield { RestaurantInfo(map.get("name").asInstanceOf[String]) }

FindNearbyRestaurantResponse(rs.toList)

}...

Not thread-safe

5 restaurants within 1km

Page 53: Developing applications with Cloud Services  #javaone 2012

Agenda

• Why use cloud services?

• Developing location-based applications

• Building SMS and telephony enabled applications

• Developing robust, fault tolerant applications

Page 54: Developing applications with Cloud Services  #javaone 2012

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

Page 55: Developing applications with Cloud Services  #javaone 2012

Reporting traffic light problems in London

Page 56: Developing applications with Cloud Services  #javaone 2012

Google 2-Factor authentication

Page 57: Developing applications with Cloud Services  #javaone 2012

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 notification of voting results

Page 58: Developing applications with Cloud Services  #javaone 2012

DIY telephony = Difficult

• Difficult to setup and operate

• Expensive

• Complex SMS protocols

• …Bette

r to use S

MS/Telepho

ny-aaS:

Page 59: Developing applications with Cloud Services  #javaone 2012

Telephony/SMS - aaS

• SMS• Inbound and outgoing calls• Recording and transcription

• SMS• Inbound and outgoing calls• Recording and transcription• Twitter• IM

Page 60: Developing applications with Cloud Services  #javaone 2012

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

Page 61: Developing applications with Cloud Services  #javaone 2012

Using Twilio

Twilio Your Application

TwiML doc

HTTP GET/POST

REST API

Manage resourcesSend SMS

Initiate voice calls

Handle incoming SMS and voice callsRespond to user input

VoiceSMS

Phone number ⇒

SMS URL + VOICE URL

Page 62: Developing applications with Cloud Services  #javaone 2012

Handling SMS registration

5551212

Page 63: Developing applications with Cloud Services  #javaone 2012

Handling SMS registration

TwilioSMS

REGISTRATION

HTTP POST http://≪smsUrl≫?From=≪PhoneNumber≫

<Response> <Sms>To complete registration please go to http://... </Sms></Response>

SMS

Page 64: Developing applications with Cloud Services  #javaone 2012

Handling SMS registration

TwiML document describing the

response

Page 65: Developing applications with Cloud Services  #javaone 2012

Inviting users to vote

5551212

Page 66: Developing applications with Cloud Services  #javaone 2012

Inviting users to vote

POST /2010-04-01/Accounts/≪AccountSID≫/SMS/Messages From=+15105551212&To=+14155551212&Body=≪MESSAGE≫Authorization: Basic ....

Basic auth using Twilio AccountSid+AuthToken

Page 67: Developing applications with Cloud Services  #javaone 2012

Sending SMS using the Spring REST Template

@Componentclass TwilioService {

def sendSms(recipient : String, message : String) = { val response = postToTwilio("SMS/Messages", Map("From" -> twilioPhoneNumber, "To" -> recipient, "Body" -> message)) (response \ "SMSMessage" \ "Sid").text }

Page 68: Developing applications with Cloud Services  #javaone 2012

Sending SMS using the Spring REST Template

TODO

@Componentclass 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) }}

Page 69: Developing applications with Cloud Services  #javaone 2012

Voting

555 1212

Page 70: Developing applications with Cloud Services  #javaone 2012

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

Page 71: Developing applications with Cloud Services  #javaone 2012

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

Page 72: Developing applications with Cloud Services  #javaone 2012

Voting code 1@Controllerclass 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> } }

Page 73: Developing applications with Cloud Services  #javaone 2012

Voting code 2class 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> }}

Page 74: Developing applications with Cloud Services  #javaone 2012

Agenda

• Why use cloud services?

• Developing location-based applications

• Building SMS and telephony enabled applications

• Developing robust, fault tolerant applications

Page 75: Developing applications with Cloud Services  #javaone 2012

The need for parallelism

Service A

Service B

Service C

Service D

b = serviceB()

c = serviceC()

d = serviceD(b, c)

Call in parallel

Page 76: Developing applications with Cloud Services  #javaone 2012

Java Futures are a great concurrency abstraction

http://en.wikipedia.org/wiki/Futures_and_promises

Page 77: Developing applications with Cloud Services  #javaone 2012

Akka’s composable futures are even better

Page 78: Developing applications with Cloud Services  #javaone 2012

Using Akka futurestrait FriendService { def findNearbyFriends(request : NearbyFriendsRequest) :

Future[FindNearbyFriendsResponse]}

trait RestaurantService { def findNearbyRestaurants(location: Location) :

Future[FindNearbyRestaurantResponse]}

val friendsRequest = NearbyFriendsRequest.fromLocation(vmeRecord.location)for ( (nearbyFriends, nearbyRestaurants) <-

friendsService.findNearbyFriends(friendsRequest) zip restaurantService.findNearbyRestaurants(vmeRecord.location)

) {

.... }} Two calls execute in parallel

Page 79: Developing applications with Cloud Services  #javaone 2012

Using external web services = Distributed system

VoteMeetEat

TwilioMongoDB

Factual.Com

MobilePhone

Page 80: Developing applications with Cloud Services  #javaone 2012

Internally = Distributed System

Survey management

VMEmanagement

Registration SMS

Registrationweb app

VME web app

Usermanagement

RabbitMQ

Page 81: Developing applications with Cloud Services  #javaone 2012

Handling failure

Service A Service B

Errors happen in distributed systems

Page 82: Developing applications with Cloud Services  #javaone 2012

About Netflix

> 1B API calls/day

1 API call ⇒ average 6 service calls

Fault tolerance is essential

http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html

Page 83: Developing applications with Cloud Services  #javaone 2012

Use timeouts and retries

Never wait forever

Errors can be transient ⇒ retry

http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html

Page 84: Developing applications with Cloud Services  #javaone 2012

Service A

Service B

Use per-dependency bounded thread pool

http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html

Runnable 1

Runnable 2

Runnable ...

bounded queue

Task 1

Task 2

Task ...

bounded thread pool

Limits number of outstanding requests

Fails fast if service is slow or down

Page 85: Developing applications with Cloud Services  #javaone 2012

Use a circuit breaker

High error rate ⇒ stop calling temporarily

Down ⇒ wait for it to come back up

Slow ⇒ gives it a chance to recover

http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html

Closed Open

Half open

errors

successtimeout

fail

Page 86: Developing applications with Cloud Services  #javaone 2012

On failure

http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html

AvoidFailing

Return cached data

Return default data

Fail fast

Page 87: Developing applications with Cloud Services  #javaone 2012

WorkerActor

Aspects + Actors

CallerDependency

InvokerAspect

DependencyStub

CircuitBreakerActor

WorkerActor

Implements circuit

breaker state machine

Equivalent of thread pool

Page 88: Developing applications with Cloud Services  #javaone 2012

@DependencyProxy annotation

@Service@DependencyProxy(circuitBreaker = "factualCircuitBreaker", timeoutInMilliseconds=750)class FactualRestaurantService extends RestaurantService { ...}

trait RestaurantService { def findNearbyRestaurants(location: Location) : Future[FindNearbyRestaurantResponse]}

Page 89: Developing applications with Cloud Services  #javaone 2012

Aspect-based Async Execution

@Aspectclass DependencyInvokingAspect {

@Pointcut("execution(* (@DependencyProxy *).*(..))") def dependencyProxyMethods {}

@Around("dependencyProxyMethods()") def invoke(jp: ProceedingJoinPoint) = { val a = AnnotationUtils.findAnnotation(jp.getTarget.getClass, classOf[DependencyProxy]) val actor = findActor(a) val timeout = Timeout(a.timeoutInMilliseconds milliseconds) actor.ask(InvokeDependency( () => jp.proceed())) (timeout) }

}

Ask actor to invoke jp.proceed() and return Future

Page 90: Developing applications with Cloud Services  #javaone 2012

See

https://github.com/cer/votemeeteat

for the Actor code

Page 91: Developing applications with Cloud Services  #javaone 2012

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

Page 92: Developing applications with Cloud Services  #javaone 2012

Questions?

@crichardson [email protected]://slideshare.net/chris.e.richardson/

Sign up for CloudFoundry.com using promo code cfjavaone


Recommended