Transcript
Page 1: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Consuming web services asynchronously with Futures

and Rx ObservablesChris Richardson

Author of POJOs in ActionFounder of the original CloudFoundry.com

@[email protected]://plainoldobjects.com

Page 2: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Presentation goal

Learn how to use (Scala) Futures and Rx

Observables to write simple yet robust and scalable

concurrent code

Page 3: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

About Chris

Page 4: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

(About Chris)

Page 5: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

About Chris()

Page 6: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

About Chris

Page 7: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

About Chris

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

Page 8: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

About Chris

Page 9: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Agenda

The need for concurrency

Simplifying concurrent code with Futures

Taming callback hell with JavaScript promises

Consuming asynchronous streams with Reactive Extensions

Page 10: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Let’s imagine you are building an online store

Page 11: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Shipping

Recomendations

ProductInfo

Reviews

Page 12: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Reviews

ProductInfo Sales

ranking

Page 13: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Related books

Viewing history

Forum

Page 14: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

+ mobile apps

Page 15: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Application architecture

Product Info ServiceDesktop browser

Native mobile client

REST

REST

REST

Recomendation Service

Review Service

Front end server

API gatewayREST

Mobile browser

Web Application

HTML/JSON

JSON

Page 16: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Handling getProductDetails()

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails()

Page 17: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Handling getProductDetails() - sequentially

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails()

Higher response time :-(

Page 18: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Handling getProductDetails() - in parallel

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails()

Lower response time :-)

Page 19: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Implementing a concurrent REST client

Thread-pool based approach

executorService.submit(new Callable(...))

Simpler but less scalable - lots of idle threads consuming memory

Event-driven approach

NIO with completion callbacks

More complex but more scalable

Page 20: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

The front-end server must handle partial failure of backend services

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails() XHow to provide a good user experience?

Page 21: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Agenda

The need for concurrency

Simplifying concurrent code with Futures

Taming callback hell with JavaScript promises

Consuming asynchronous streams with Reactive Extensions

Page 22: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Futures are a great concurrency abstraction

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

Page 23: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Worker threadMain threadHow futures work

Outcome

Future

Client

get

Asynchronous operation

set

initiates

Page 24: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Benefits

Client does not know how the asynchronous operation is implemented

Client can invoke multiple asynchronous operations and gets a Future for each one.

Page 25: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Handling ProductDetails request

ProductDetailsController

ProductDetailsService

getProductDetails()

ProductInfoService

getProductInfo()

ReviewService

getReviews()

RecommendationService

getRecommendations()

RestTemplate

Page 26: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

REST client using Spring @Async

@Componentclass ProductInfoServiceImpl extends ProducInfoService { val restTemplate : RestTemplate = ...

@Async def getProductInfo(productId: Long) = { new AsyncResult(restTemplate.getForObject(....)...) }

}

Execute asynchronously in

thread pool

A fulfilled Future

trait ProductInfoService { def getProductInfo(productId: Long):

java.util.concurrent.Future[ProductInfo]}

Page 27: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

ProductDetailsService@Componentclass ProductDetailsService @Autowired()(productInfoService: ProductInfoService, reviewService: ReviewService, recommendationService: RecommendationService) {

def getProductDetails(productId: Long): ProductDetails = { val productInfoFuture = productInfoService.getProductInfo(productId)

}

}

val recommendationsFuture = recommendationService.getRecommendations(productId)val reviewsFuture = reviewService.getReviews(productId)

val productInfo = productInfoFuture.get(300, TimeUnit.MILLISECONDS) val recommendations = recommendationsFuture.get(10, TimeUnit.MILLISECONDS) val reviews = reviewsFuture.get(10, TimeUnit.MILLISECONDS)

ProductDetails(productInfo, recommendations, reviews)

Page 28: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

ProductController

@Controllerclass ProductController @Autowired()(productDetailsService : ProductDetailsService) {

@RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) =

productDetailsService.getProductDetails(productId)

Page 29: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Blocks Tomcat thread until Future completes

Not bad but...

class ProductDetailsService def getProductDetails(productId: Long): ProductDetails = {

val productInfo = productInfoFuture.get(300, TimeUnit.MILLISECONDS)

Not so scalable :-(

Page 30: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

... and also...

Java Futures work well for a single-level of asynchronous execution

BUT #fail for more complex, scalable scenarios

Difficult to compose and coordinate multiple concurrent operations

See this blog post for more details:http://techblog.netflix.com/2013/02/rxjava-netflix-api.html

Page 31: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Better: Futures with callbacks ⇒ no blocking! val f : Future[Int] = Future { ... } f onSuccess { case x : Int => println(x) } f onFailure { case e : Exception => println("exception thrown") }

Guava ListenableFutures, Spring 4 ListenableFutureJava 8 CompletableFuture, Scala Futures

Page 32: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Even better: composable futures

def asyncOperation() = Future { ... ; 1 }

val r = asyncOperation().map{ _ * 2 }

assertEquals(4, Await.result(r, 1 second))

Scala, Java 8 CompletableFuture (partially)

Transforms Future

Page 33: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

map() is asynchronous - uses callbacks

class Future[T] {

def map[S](f: T => S): Future[S] = { val p = Promise[S]()

onSuccess { case r => p success f(r) } onFailure { case t => p failure t }

p.future }

When ‘this’ completes propagate outcome to ‘p’

Return new Future

Page 34: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Chaining using flatmap()

def asyncOperation() = Future { ... ; 3 }

def asyncSquare(x : Int) : Future[Int] = Future { x * x}

val r = asyncOperation().flatMap { x => asyncSquare(x) }

assertEquals(9, Await.result(r, 1 second))

Scala, Java 8 CompletableFuture (partially)

Chaining asynchronous operations

Page 35: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Combining futuresval f1 = Future { ... ; 1}val f2 = Future { .... ; 2}

val fzip = f1 zip f2assertEquals((1, 2), Await.result(fzip, 1 second))

def asyncOp(x : Int) = Future { x * x}val f = Future.sequence((1 to 5).map { x => asyncOp(x) })assertEquals(List(1, 4, 9, 16, 25), Await.result(f, 1 second))

Scala, Java 8 CompletableFuture (partially)

Combines two futures

Transforms list of futures to a future containing a list

Page 36: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Scala futures are Monadsdef callB() : Future[...] = ...def callC() : Future[...] = ...def callD() : Future[...] = ...

val result = for { (b, c) <- callB() zip callC(); d <- callD(b, c) } yield d

result onSuccess { .... }

Two calls execute in parallel

And then invokes D

Get the result of D

Page 37: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

‘for’ is shorthand for map() and flatMap()

(callB() zip callC()) flatMap { r1 => val (b, c) = r1 callD(b, c) map { d => d }}

for { (b, c) <- callB() zip callC(); d <- callD(b, c) } yield d

Page 38: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Scala Future + RestTemplateimport scala.concurrent.Future

@Componentclass ProductInfoService {

def getProductInfo(productId: Long): Future[ProductInfo] = { Future { restTemplate.getForObject(....) } }

}

Executes in thread pool

Scala Future

Page 39: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Scala Future + RestTemplateclass ProductDetailsService @Autowired()(....) {

def getProductDetails(productId: Long) = { val productInfoFuture = productInfoService.getProductInfo(productId) val recommendationsFuture = recommendationService.getRecommendations(productId) val reviewsFuture = reviewService.getReviews(productId)

for (((productInfo, recommendations), reviews) <- productInfoFuture zip recommendationsFuture zip reviewsFuture) yield ProductDetails(productInfo, recommendations, reviews) }

}

Non-blocking!

Page 40: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Async Spring MVC + Scala Futures

@Controllerclass ProductController ... {

@RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = { val productDetails = productDetailsService.getProductDetails(productId)

val result = new DeferredResult[ProductDetails] productDetails onSuccess { case r => result.setResult(r) } productDetails onFailure { case t => result.setErrorResult(t) } result }

Convert Scala Future to

Spring MVCDeferredResult

Page 41: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Servlet layer is asynchronous but the backend uses thread

pools⇒

Need event-driven REST client

Page 42: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

New in Spring 4

Mirrors RestTemplate

Methods return a ListenableFuture

ListenableFuture = JDK 7 Future + callback methods

Can use HttpComponents NIO-based AsyncHttpClient

Spring AsyncRestTemplate

Page 43: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Using the AsyncRestTemplate

http://hc.apache.org/httpcomponents-asyncclient-dev/

val asyncRestTemplate = new AsyncRestTemplate( new HttpComponentsAsyncClientHttpRequestFactory())

override def getProductInfo(productId: Long) = {

val listenableFuture = asyncRestTemplate.getForEntity("{baseUrl}/productinfo/{productId}", classOf[ProductInfo], baseUrl, productId)

toScalaFuture(listenableFuture).map { _.getBody }}

Convert to Scala Future and get entity

Page 44: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Converting ListenableFuture to Scala Future

implicit def toScalaFuture[T](f : ListenableFuture[T]) : Future[T] = { val p = promise[T]()

f.addCallback(new ListenableFutureCallback[T] { def onSuccess(result: T) { p.success(result)} def onFailure(t: Throwable) { p.failure(t) } }) p.future }

The producer side of Scala Futures

Supply outcome

Return future

Page 45: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Now everything is non-blocking :-)

Page 46: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

If recommendation service is down...

Never responds ⇒ front-end server waits indefinitely

Consumes valuable front-end server resources

Page never displayed and customer gives up

Returns an error

Error returned to the front-end server ⇒ error page is displayed

Customer gives up

Page 47: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Fault tolerance at NetflixNetwork 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

Implementation: https://github.com/Netflix/Hystrix

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

Page 48: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

How to handling partial failures?

productDetailsFuture zip recommendationsFuture zip reviewsFuture

Fails if any Future has failed

Page 49: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Handling partial failures

recommendationService. getRecommendations(userId,productId) .recover { case _ => Recommendations(List()) }

“catch-like” Maps Throwable to value

Page 50: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Agenda

The need for concurrency

Simplifying concurrent code with Futures

Taming callback hell with JavaScript promises

Consuming asynchronous streams with Reactive Extensions

Page 51: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Why solve this problem for JavaScript?

Browser invokes web services

Implement front-end server/API gateway using NodeJS

Page 52: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

What’s NodeJS?

Designed for DIRTy apps

Page 53: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

NodeJS

JavaScript

Reactor pattern

Modules

Page 54: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Asynchronous JavaScript code = callback hell

Scenarios:

Sequential: A ⇒ B ⇒ C

Fork and join: A and B ⇒ C

Code quickly becomes very messy

Page 55: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Callback-based HTTP client

request = require("request") handler = (error, clientResponse, body) -> if clientResponse.statusCode != 200 // ERROR else // SUCCESS

request("http://.../productdetails/" + productId, handler)

Page 56: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Messy callback codegetProductDetails = (req, res) -> productId = req.params.productId result = {productId: productId} makeHandler = (key) -> (error, clientResponse, body) -> if clientResponse.statusCode != 200 res.status(clientResponse.statusCode) res.write(body) res.end() else result[key] = JSON.parse(body) if (result.productInfo and result.recommendations and result.reviews) res.json(result)

request("http://localhost:3000/productdetails/" + productId, makeHandler("productInfo")) request("http://localhost:3000/recommendations/" + productId,

makeHandler('recommendations')) request("http://localhost:3000/reviews/" + productId, makeHandler('reviews'))

app.get('/productinfo/:productId', getProductInfo)

Returns a callback function

Have all three requests completed?

Page 57: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Simplifying code with Promises (a.k.a. Futures)

Functions return a promise - no callback parameter

A promise represents an eventual outcome

Use a library of functions for transforming and composing promises

Promises/A+ specification - http://promises-aplus.github.io/promises-spec

Page 58: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Basic promise API

promise2 = promise1.then(fulfilledHandler, errorHandler, ...)

called when promise1 is fulfilledreturns a promise or value

resolved with outcome of callback

called when promise1 is rejected

Page 59: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

About when.jsRich API = Promises spec + use full extensions

Creation:

when.defer()

when.reject()...

Combinators:

all, some, join, map, reduce

Other:

Calling non-promise code

timeout

....

https://github.com/cujojs/when

Page 60: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Simpler promise-based code

rest = require("rest")

@httpClient = rest.chain(mime).chain(errorCode);

getProductInfo : (productId) -> @httpClient path: "http://.../productinfo/" + productId

Returns a promise

No ugly callbacks

Page 61: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Simpler promise-based code class ProductDetailsService getProductDetails: (productId) -> makeProductDetails = (productInfo, recommendations, reviews) -> details = productId: productId productDetails: productInfo.entity recommendations: recommendations.entity reviews: reviews.entity details responses = [@getProductInfo(productId), @getRecommendations(productId),

@getReviews(productId)] all(responses).spread(makeProductDetails)

Array[Promise] ⇒ Promise[Array]

Page 62: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Simpler promise-based code: HTTP handler

getProductDetails = (req, res) -> productId = req.params.productId

succeed = (productDetails) -> res.json(productDetails)

fail = (something) -> res.send(500, JSON.stringify(something.entity || something))

productDetailsService. getDetails(productId). then(succeed, fail)

app.get('/productdetails/:productId', getProductDetails)

Page 63: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Writing robust client code

Page 64: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Recovering from failures

getRecommendations(productId) .otherwise( -> {})

Invoked to return default value if getRecommendations() fails

Page 65: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Agenda

The need for concurrency

Simplifying concurrent code with Futures

Taming callback hell with JavaScript promises

Consuming asynchronous streams with Reactive Extensions

Page 66: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Let’s imagine you have a stream of trades

and you need to calculate the rolling

average price of each stock

Page 67: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Spring Integration + Complex event processing (CEP) engine is a good choice

Page 68: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

But where is the high-level abstraction that solves this

problem?

Page 69: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Future[List[T]]

Not applicable to infinite streams

Page 70: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Introducing Reactive Extensions (Rx)

The Reactive Extensions (Rx) is a library for composing asynchronous and event-

based programs using observable sequences and LINQ-style query operators. Using Rx, developers

represent asynchronous data streams with Observables , query

asynchronous data streams using LINQ operators , and .....

https://rx.codeplex.com/

Page 71: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

About RxJava

Reactive Extensions (Rx) for the JVM

Implemented in Java

Adaptors for Scala, Groovy and Clojure

https://github.com/Netflix/RxJava

Page 72: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

RxJava core concepts

class Observable<T> { Subscription subscribe(Observer<T> observer) ...}

interface Observer<T> {void onNext(T args)void onCompleted()void onError(Throwable e)

}

Notifies

An asynchronous stream of items

Used to unsubscribe

Page 73: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Comparing Observable to...Observer pattern - similar but adds

Observer.onComplete()

Observer.onError()

Iterator pattern - mirror image

Push rather than pull

Future - similar but

Represents a stream of multiple values

Page 74: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

So what?

Page 75: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Transforming Observables

class Observable<T> {

Observable<T> take(int n); Observable<T> skip(int n); <R> Observable<R> map(Func1<T,R> func) <R> Observable<R> flatMap(Func1<T,Observable<R>> func) Observable<T> filter(Func1<T,java.lang.Boolean> predicate)

...}

Scala-collection style methods

Page 76: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Transforming observables

class Observable<T> {

<K> Observable<GroupedObservable<K,T>> groupBy(Func1<T,K> keySelector) ...}

Similar to Scala groupBy except results are emitted as items arrive

Stream of streams!

Page 77: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Combining observables

class Observable<T> {

static <R,T1,T2> Observable<R> zip(Observable<T0> o1, Observable<T1> o2, Func2<T1,T2,R> reduceFunction)

...}

Invoked with pairs of items from o1 and o2

Stream of results from reduceFunction

Page 78: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Creating observables from data

class Observable<T> { static Observable<T> from(Iterable<T> iterable]); static Observable<T> from(T items ...]); static Observable<T> from(Future<T> future]); ...}

Page 79: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Arranges to call obs.onNext/onComplete/...

Creating observables from event sources

Observable<T> o = Observable.create( new SubscriberFunc<T> () { Subscription onSubscribe(Observer<T> obs) {

... connect to event source....

return new Subscriber () { void unsubscribe() { ... };

}; });

Called once for

each Observer

Called when unsubscribing

Page 80: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Using Rx Observables instead of Futures

Page 81: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Rx-based ProductInfoService@Componentclass ProductInfoService override def getProductInfo(productId: Long) = {

val baseUrl = ...

val responseEntity = asyncRestTemplate.getForEntity(baseUrl + "/productinfo/{productId}", classOf[ProductInfo], productId)

toRxObservable(responseEntity).map { (r : ResponseEntity[ProductInfo]) => r.getBody }

}

Page 82: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

ListenableFuture ⇒ Observable

implicit def toRxObservable[T](future: ListenableFuture[T]) : Observable[T] = { def create(o: Observer[T]) = { future.addCallback(new ListenableFutureCallback[T] { def onSuccess(result: T) { o.onNext(result) o.onCompleted() }

def onFailure(t: Throwable) { o.onError(t) } }) new Subscription { def unsubscribe() {} } }

Observable.create(create _) }

Supply single value

Indicate failure

Do nothing

Page 83: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Rx - ProductDetailsService@Componentclass ProductDetailsService @Autowired() (productInfoService: ProductInfoService, reviewService: ReviewService, ecommendationService: RecommendationService) {

def getProductDetails(productId: Long) = { val productInfo = productInfoService.getProductInfo(productId) val recommendations =

recommendationService.getRecommendations(productId) val reviews = reviewService.getReviews(productId)

Observable.zip(productInfo, recommendations, reviews, (p : ProductInfo, r : Recommendations, rv : Reviews) =>

ProductDetails(p, r, rv)) }

}Just like Scala Futures

Page 84: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Rx - ProductController

@Controllerclass ProductController

@RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = val pd = productDetailsService.getProductDetails(productId) toDeferredResult(pd)

Page 85: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Rx - Observable ⇒ DeferredResult

implicit def toDeferredResult[T](observable : Observable[T]) = { val result = new DeferredResult[T] observable.subscribe(new Observer[T] { def onCompleted() {}

def onError(e: Throwable) { result.setErrorResult(e) }

def onNext(r: T) { result.setResult(r.asInstanceOf[T]) } }) result }

Page 86: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

RxObservables vs. Futures

Much better than JDK 7 futures

Comparable to Scala Futures

Rx Scala code is slightly more verbose

Page 87: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Making this code robust

Hystrix works with Observables (and thread pools)

But

For event-driven code we are on our own

Page 88: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Back to the stream of Trades averaging example...

Page 89: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Calculating averages

Observable<AveragePrice> calculateAverages(Observable<Trade> trades) { ...}

class Trade { private String symbol; private double price;

class AveragePrice { private String symbol; private double averagePrice;

Page 90: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Using groupBy()APPL : 401 IBM : 405 CAT : 405 APPL: 403

groupBy( (trade) => trade.symbol)

APPL : 401

IBM : 405

CAT : 405 ...

APPL: 403

Observable<GroupedObservable<String, Trade>>

Observable<Trade>

Page 91: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Using window()APPL : 401 APPL : 405 APPL : 405 ...

APPL : 401 APPL : 405 APPL : 405

APPL : 405 APPL : 405 APPL : 403

APPL : 405 ...

window(...)

Observable<Trade>

Observable<Observable<Trade>>

N secs

N secs

M secs

Page 92: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Using fold()

APPL : 402 APPL : 405 APPL : 405

APPL : 404

fold(0)(sumCalc) / length

Observable<Trade>

Observable<AveragePrice> Singleton

Page 93: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Using merge()

merge()

APPL : 401

IBM : 405

CAT : 405 ...

APPL: 403

APPL : 401 IBM : 405 CAT : 405 APPL: 403

Observable<Observable<AveragePrice>>

Observable<AveragePrice>

Page 94: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Calculating average pricesdef calculateAverages(trades: Observable[Trade]): Observable[AveragePrice] = {

trades.groupBy(_.symbol).map { symbolAndTrades => val (symbol, tradesForSymbol) = symbolAndTrades ...

tradesForSymbol.window(...).map { windowOfTradesForSymbol => windowOfTradesForSymbol.fold((0.0, 0, List[Double]())) { (soFar, trade) => val (sum, count, prices) = soFar (sum + trade.price, count + 1, trade.price +: prices) } map { x => val (sum, length, prices) = x AveragePrice(symbol, sum / length, prices) } }.flatMap(identity) }.flatMap(identity)}

Page 95: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

RxJS / NodeJS examplevar rx = require("rx");

function tailFile (fileName) { var self = this;

var o = rx.Observable.create(function (observer) { var watcher = self.tail(fileName, function (line) { observer.onNext(line); }); return function () { watcher.close(); } }); return o.map(parseLogLine);}

function parseLogLine(line) { ... }

Page 96: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Rx in the browser - implementing completion

TextChanges(input) .DistinctUntilChanged() .Throttle(TimeSpan.FromMilliSeconds(10)) .Select(word=>Completions(word)) .Switch() .Subscribe(ObserveChanges(output));

Your Mouse is a Databasehttp://queue.acm.org/detail.cfm?id=2169076

Ajax call returning Observable

Change events ⇒ Observable

Display completions

Page 97: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Summary

Consuming web services asynchronously is essential

Scala-style are a powerful concurrency abstraction

Rx Observables are even more powerful

Rx Observables are a unifying abstraction for a wide variety of use cases

Page 98: Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)

@crichardson

Questions?

@crichardson [email protected]

http://plainoldobjects.com


Top Related