109
@crichardson Consuming web services asynchronously with Futures and Rx Observables Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson [email protected] http://plainoldobjects.com

Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

Embed Size (px)

DESCRIPTION

Speaker: Chris Richardson A modular, polyglot architecture has many advantages but it also adds complexity since each incoming request typically fans out to multiple distributed services. For example, in an online store application the information on a product details page - description, price, recommendations, etc - comes from numerous services. To minimize response time and improve scalability, these services must be invoked concurrently. However, traditional concurrency mechanisms are low-level, painful to use and error-prone. In this talk you will learn about some powerful yet easy to use abstractions for consuming web services asynchronously. We will compare the various implementations of futures that are available in Java, Scala and JavaScript. You will learn how to use reactive observables, which are asynchronous data streams, to access web services from both Java and JavaScript. We will describe how these mechanisms let you write asynchronous code in a very straightforward, declarative fashion.

Citation preview

Page 1: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Presentation goal

Learn how to use (Scala) Futures and Rx Observables to write

simple yet robust and scalable concurrent code

Page 3: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About Chris

Page 4: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

(About Chris)

Page 5: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About Chris()

Page 6: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About Chris

Page 7: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About Chris

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

Page 8: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About Chris

Page 9: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Let’s imagine you are building an online store

Page 11: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Shipping

Recomendations

ProductInfo

Reviews

Page 12: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Reviews

ProductInfo

Sales ranking

Page 13: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Related books

Viewing history

Forum

Page 14: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

+ mobile apps

Page 15: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

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

Page 16: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Handling getProductDetails()

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails()

Page 17: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Handling getProductDetails() - sequentially

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails()

Higher response time :-(

Page 18: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Handling getProductDetails() - in parallel

Front-end server

Product Info Service

RecommendationsService

ReviewService

getProductInfo()

getRecommendations()

getReviews()

getProductDetails()

Lower response time :-)

Page 19: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Futures are a great concurrency abstraction

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

Page 23: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

How futures work

Outcome

Future

Client

get

Asynchronous operation

set

initiates

Page 24: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 Future containing a value

trait ProductInfoService { def getProductInfo(productId: Long): java.util.concurrent.Future[ProductInfo]}

Page 26: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 27: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

ProductController

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

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

productDetailsService.getProductDetails(productId)

Page 28: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Not bad but...

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

Blocks Tomcat thread until Future completes

Not so scalable :-(

Page 29: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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

http://techblog.netflix.com/2013/02/rxjava-netflix-api.html

Page 30: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 31: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Even better: Composable Futures

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

val f4 = f2.map(_ * 2)assertEquals(4, Await.result(f4, 1 second))

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

Transforms Future

Combines two futures

Scala, Java 8 CompletableFuture (partially)

Page 32: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 33: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 34: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 35: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

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

DeferredResult

Page 36: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Servlet layer is asynchronous but the backend uses thread pools

⇒Need event-driven REST client

Page 37: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About the Reactor pattern

Defined by Doug Schmidt in 1995

Pattern for writing scalable servers

Alternative to thread-per-connection model

Single threaded event loop dispatches events on handles (e.g. sockets, file descriptors) to event handlers

Page 38: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Reactor pattern structure

Event Handlerhandle_event(type)get_handle()

Initiation Dispatcherhandle_events() register_handler(h)

select(handlers)for each h in handlers h.handle_event(type)end loop

handle

Synchronous Event Demultiplexer

select()

owns

notifies

uses

handlers

Application

creates

Page 39: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Java NIO Selectors = Reactor pattern

But that’s super low-level :-(

Page 40: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

New in Spring 4

Mirrors RestTemplate

Methods return a ListenableFuture = JDK 7 Future + callback methods

Can use HttpComponents NIO-based AsyncHttpClient

Spring AsyncRestTemplate

Page 41: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 42: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 outcomeReturn future

Page 43: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Now everything is non-blocking :-)

Page 44: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 45: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 46: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Using Hystrix@Componentclass ProductInfoServiceImpl extends ProductInfoService {

val restTemplate = RestTemplateFactory.makeRestTemplate() val baseUrl = ...

class GetProductInfoCommand(productId: Long)extends HystrixCommand[ProductInfo](....) {

override def run() = restTemplate. getForEntity("{baseUrl}/productinfo/{productId}",

classOf[ProductInfo], baseUrl, productId).getBody

}

def getProductInfoUsingHystrix(productId: Long) : Future[ProductInfo] = { new GetProductInfoCommand(productId).queue() }

}

Runs in thread pool

Returns JDK Future

Page 47: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

But how to accomplish this with event-driven code

Page 48: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

How to handling partial failures?

productDetailsFuture zip recommendationsFuture zip reviewsFuture

Fails if any Future has failed

Page 49: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

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

“catch-like” Maps Throwable to value

Page 50: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Implementing a Timeout

resultFuture onSuccess { case r => result.setResult(r) }resultFuture onFailure { case t => result.setErrorResult(t) }

No timeout - callbacks might never be invoked :-(

Await.result(resultFuture, timeout)

Blocks until timeout:-(

Page 51: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Non-blocking Timeout

http://eng.42go.com/future-safefuture-timeout-cancelable/

object TimeoutFuture { def apply[T](future: Future[T], onTimeout: => Unit = Unit) (implicit ec: ExecutionContext, after: Duration): Future[T] = { val timer = new HashedWheelTimer(10, TimeUnit.MILLISECONDS) val promise = Promise[T]() val timeout = timer.newTimeout(new TimerTask { def run(timeout: Timeout){ onTimeout promise.failure(new TimeoutException(s"Future timed out after ${after.toMillis}ms")) } }, after.toNanos, TimeUnit.NANOSECONDS) Future.firstCompletedOf(Seq(future, promise.future)). tap(_.onComplete { case result => timeout.cancel() }) } }

val future = ...val timedoutFuture = TimeoutFuture(future)(executionContext, 200.milleseconds) Timer fails

promise

Outcome of first completed

Page 52: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Using the Akka Circuit Breaker

import akka.pattern.CircuitBreaker

val breaker = new CircuitBreaker(actorSystem.scheduler, maxFailures = 1, callTimeout = 100.milliseconds, resetTimeout = 1.minute)

val resultFuture = breaker. withCircuitBreaker{ asynchronousOperationReturningFuture() }

Page 53: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Limiting # of simultaneous requestsval limiter = new ConcurrencyLimiter(maxConcurrentRequests=10, maxQueueSize=30)

val resultFuture = limiter.withLimit { asynchronousOperationReturningFuture() }class ConcurrencyLimiter ...

val concurrencyManager : ActorRef = ...

def withLimit[T](body : => Future[T])(...) = (concurrencyManager ? ConcurrentExecutionManager.Request { () => body }).mapTo[T]

Page 54: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Putting it all together@Componentclass ProductInfoServiceImpl @Autowired()(...) extends ProductService {

val limiter = new ConcurrencyLimiter(...)

val breaker = new CircuitBreaker(...)

override def getProductInfo(productId: Long) = { ... breaker.withCircuitBreaker { limiter.withLimit { TimeoutFuture { ... AsyncRestTemplate.get ... } } } }

Page 55: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Agenda

The need for concurrency

Simplifying concurrent code with Futures

Taming callback hell with JavaScript promises

Consuming asynchronous streams with Reactive Extensions

Page 56: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Page 57: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Why solve this problem for JavaScript?

Browser invokes web services

Implement front-end server/API gateway using NodeJS!

Page 58: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

What’s NodeJS?

Designed for DIRTy apps

Page 59: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

NodeJS

JavaScript

Reactor pattern

Modules

Page 60: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Asynchronous JavaScript code = callback hell

Scenarios:

Sequential: A ⇒ B ⇒ C

Fork and join: A and B ⇒ C

Code quickly becomes very messy

Page 61: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 62: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 63: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 64: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Basic promise API

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

called when promise1 is fulfilledreturns a promise or value

resolved with outcome of callbackcalled when promise1 is rejected

returns a promise or value

Page 65: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

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 66: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 67: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 68: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Simpler promise-based code: HTTP handlergetProductDetails = (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 69: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Writing robust client code

Page 70: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Implementing timeouts

timeout = require("when/timeout")withTimeout = (promise) -> timeout(300, promise)getProductDetails = (productId) -> ... withTimeout(client(...))

Creates a new promiseOriginal promise must complete within 300 msec

Page 71: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Recovering from failures

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

Invoked to return default value if getRecommendations() fails

Page 72: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Limiting # of concurrent requests

ConcurrencyLimiter = require("./concurrencylimiter").ConcurrencyLimiter

limiter = new ConcurrencyLimiter(maxQueueSize = 100, maxConcurrency = 10)

getProductDetails = (productId) -> ... limiter.withLimit( -> client(...))

Homegrown code

Page 73: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Circuit breaker

CircuitBreaker = require("./circuitbreaker").CircuitBreaker

productCircuitBreaker = new CircuitBreaker()

getProductDetails = (productId) -> ... productCircuitBreaker.withCircuitBreaker( -> client(...))

Homegrown code

Page 74: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Putting it all together

getProductDetails: (productId) -> ... responses = [@getProductInfo(productId), @getRecommendations(productId).otherwise(() -> {}), @getReviews(productId).otherwise(() -> {})] all(responses).spread(succeed)

getProductInfo = (productId) -> limiter.withLimit -> productCircuitBreaker.withCircuitBreaker -> withTimeout client path: "http://..../productinfo/" + productd

Page 75: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Agenda

The need for concurrency

Simplifying concurrent code with Futures

Taming callback hell with JavaScript promises

Consuming asynchronous streams with Reactive Extensions

Page 76: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Let’s imagine you have a stream of tradesand

you need to calculate the rolling average price of each stock

Page 77: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

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

choice

Page 78: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

But where is the high-level abstraction that solves this problem?

Page 79: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Future[List[T]]

Not applicable to infinite streams

Page 80: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 81: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

About RxJava

Reactive Extensions (Rx) for the JVM

Implemented in Java

Adaptors for Scala, Groovy and Clojure

https://github.com/Netflix/RxJava

Page 82: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 83: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 84: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

So what?

Page 85: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 86: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 87: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 88: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 89: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

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

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

Page 90: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Using Rx Observables instead of Futures

Page 91: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 92: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 93: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 94: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Rx - ProductController

@Controllerclass ProductController

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

Page 95: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 96: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

RxObservables vs. Futures

Much better than JDK 7 futures

Comparable to Scala Futures

Rx Scala code is slightly more verbose

Page 97: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Making this code robust

Hystrix works with Observables (and thread pools)

But

For event-driven code we are on our own

Page 98: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Back to the stream of Trades averaging example...

Page 99: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

AMQP messages ⇒ Observables 1

<amqp:inbound-channel-adapter ... />

<int:channel id="inboundEventsChannel"/> <int:service-activator input-channel="inboundEventsChannel" ref="amqpToObservableAdapter" method="handleMessage"/>

Page 100: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

AMQP messages ⇒ Observables 2class AmqpToObservableAdapter (actorRef : observerManager) {

def createObservable() = Observable.create((o: Observer[_ >: String]) => {

observerManager ! Subscribe(o) new Subscription { override def unsubscribe() { observerManager ! Unsubscribe(o) } } })

def handleMessage(message : String) { observerManager ! Message(message) }}

Manages and notifies observers

Page 101: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 102: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 103: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 104: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Using reduce()

APPL : 402 APPL : 405 APPL : 405

APPL : 404

reduce(sumCalc) / length

Observable<Trade>

Observable<AveragePrice> Singleton

Page 105: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 106: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 107: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@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 108: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Summary

Consuming web services asynchronously is essential

Scala-style composable Futures 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 109: Futures and Rx Observables: powerful abstractions for consuming web services asynchronously

@crichardson

Questions?

@crichardson [email protected]

http://plainoldobjects.com