0
Consuming web services asynchronously
with Futures
and Rx Observables
Chris Richardson
Author of POJOs in Action
Founder o...
Presentation goal

Learn how to use (Scala) Futures
and Rx Observables to write
simple yet robust and scalable
concurrent ...
About Chris

@crichardson
(About Chris)

@crichardson
About Chris()

@crichardson
About Chris

@crichardson
About Chris

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

@crichardson
About Chris

@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Con...
Let’s imagine you are building an online
store

@crichardson
Reviews

Product
Info

Shipping

Recomendations
@crichardson
Product
Info
Reviews

Sales
ranking

@crichardson
Related
books

Forum
Viewing
history

@crichardson
+ mobile apps

@crichardson
Application architecture
Desktop
browser

REST

Product Info Service

REST

Recomendation Service

REST

Review Service

F...
Handling getProductDetails()
Product Info Service
getProductInfo()

getProductDetails()

Front-end server

getRecommendati...
Handling getProductDetails() - sequentially
Product Info Service
getProductInfo()

getProductDetails()

Front-end server

...
Handling getProductDetails() - in parallel
Product Info Service
getProductInfo()

getProductDetails()

Front-end server

g...
Implementing a concurrent REST client
Thread-pool based approach
executorService.submit(new Callable(...))
Simpler but les...
The front-end server must handle partial
failure of backend services
Product Info Service
getProductInfo()

getProductDeta...
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Con...
Futures are a great concurrency
abstraction
http://en.wikipedia.org/wiki/Futures_and_promises
@crichardson
How futures work
initiates

Asynchronous
operation

Client

get

set
Outcome

Future
@crichardson
Benefits

Client does not know how the asynchronous operation is implemented
Client can invoke multiple asynchronous operat...
REST client using Spring @Async
trait ProductInfoService {
def getProductInfo(productId: Long): java.util.concurrent.Futur...
ProductDetailsService
@Component
class ProductDetailsService
@Autowired()(productInfoService: ProductInfoService,
reviewSe...
ProductController
@Controller
class ProductController
@Autowired()(productDetailsService : ProductDetailsService) {
@Reque...
Not bad but...
Blocks Tomcat thread until Future
completes

val productInfo =
productInfoFuture.get(300, TimeUnit.MILLISEC...
... and also...
Java Futures work well for a single-level of asynchronous execution

BUT
#fail for more complex, scalable ...
Better: Futures with callbacks

no blocking!

val f : Future[Int] = Future { ... }
f onSuccess {
case x : Int => println(x...
Even better: Composable Futures
val f1 = Future {
val f2 = Future {

... ; 1 }
... ; 2 }

Transforms Future

val f4 = f2.m...
Scala futures are Monads
def callB() : Future[...] = ...
def callC() : Future[...] = ...
def callD() : Future[...] = ...
v...
Scala Future + RestTemplate
import scala.concurrent.Future

Scala Future

@Component
class ProductInfoService {
def getPro...
Scala Future + RestTemplate
class ProductDetailsService @Autowired()(....) {

def getProductDetails(productId: Long) = {
v...
Async Spring MVC + Scala Futures
@Controller
class ProductController ... {
@RequestMapping(Array("/productdetails/{product...
Servlet layer is asynchronous but the
backend uses thread pools
Need event-driven REST client
@crichardson
About the Reactor pattern
Defined by Doug Schmidt in 1995
Pattern for writing scalable servers
Alternative to thread-per-co...
Reactor pattern structure
Initiation Dispatcher
handle_events()
register_handler(h)

select(handlers)
for each h in handle...
Java NIO Selectors = Reactor pattern
But that’s super low-level :-(
@crichardson
Spring AsyncRestTemplate
New in Spring 4
Mirrors RestTemplate
Methods return a ListenableFuture = JDK 7 Future + callback ...
Using the AsyncRestTemplate
val asyncRestTemplate = new AsyncRestTemplate(
new HttpComponentsAsyncClientHttpRequestFactory...
Converting ListenableFuture to Scala Future
implicit def toScalaFuture[T](f : ListenableFuture[T]) : Future[T] = {
val p =...
Now everything is non-blocking :-)

@crichardson
If recommendation service is down...
Never responds

front-end server waits indefinitely

Consumes valuable front-end serve...
Fault tolerance at Netflix
Network timeouts and retries
Invoke remote services via a bounded thread pool
Use the Circuit Br...
Using Hystrix
@Component
class ProductInfoServiceImpl extends ProductInfoService {
val restTemplate = RestTemplateFactory....
But how to accomplish this with eventdriven code

@crichardson
How to handling partial failures?
Fails if any Future has failed

productDetailsFuture zip
recommendationsFuture zip revie...
Handling partial failures
val recommendationsFuture =
recommendationService.
getRecommendations(userId,productId).
recover...
Implementing a Timeout
No timeout - callbacks might never be
invoked :-(

resultFuture onSuccess { case r => result.setRes...
Non-blocking Timeout
val future = ...
val timedoutFuture = TimeoutFuture(future)(executionContext, 200.milleseconds)
objec...
Using the Akka Circuit Breaker
import akka.pattern.CircuitBreaker
val breaker =
new CircuitBreaker(actorSystem.scheduler,
...
Limiting # of simultaneous requests
val limiter =
new ConcurrencyLimiter(maxConcurrentRequests=10, maxQueueSize=30)
val re...
Putting it all together
@Component
class ProductInfoServiceImpl @Autowired()(...) extends ProductService {
val limiter = n...
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Con...
@crichardson
Why solve this problem for JavaScript?

Browser invokes web services
Implement front-end server/API gateway using NodeJS!
...
What’s NodeJS?

Designed for DIRTy apps
@crichardson
JavaScript
NodeJS

Modules

Reactor
pattern
@crichardson
Asynchronous JavaScript code =
callback hell
Scenarios:
Sequential: A

B

C

Fork and join: A and B

C

Code quickly becom...
Callback-based HTTP client
request = require("request")
handler = (error, clientResponse, body) ->
if clientResponse.statu...
Messy callback code
getProductDetails = (req, res) ->
productId = req.params.productId
result = {productId: productId}

Re...
Simplifying code with Promises (a.k.a.
Futures)
Functions return a promise - no callback parameter
A promise represents an...
Basic promise API
called when promise1 is fulfilled
returns a promise or value

promise2 = promise1.then(fulfilledHandler, ...
About when.js
Rich API = Promises spec + use full
extensions
Creation:
when.defer()
when.reject()...

Combinators:
all, so...
Simpler promise-based code
rest = require("rest")
@httpClient = rest.chain(mime).chain(errorCode);

No ugly callbacks

get...
Simpler promise-based code
class ProductDetailsService
getProductDetails: (productId) ->
makeProductDetails = (productInfo...
Simpler promise-based code: HTTP
handler
getProductDetails = (req, res) ->
productId = req.params.productId
succeed = (pro...
Writing robust client code

@crichardson
Implementing timeouts
Creates a new promise
Original promise must complete within 300 msec

timeout = require("when/timeou...
Recovering from failures
Invoked to return default value if getRecommendations()
fails

getRecommendations(productId).othe...
Limiting # of concurrent requests
ConcurrencyLimiter = require("./concurrencylimiter").ConcurrencyLimiter
limiter = new Co...
Circuit breaker
CircuitBreaker = require("./circuitbreaker").CircuitBreaker
productCircuitBreaker = new CircuitBreaker()
g...
Putting it all together
getProductInfo = (productId) ->
limiter.withLimit ->
productCircuitBreaker.withCircuitBreaker ->
w...
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript promises
Con...
Let’s imagine you have a stream of trades
and
you need to calculate the rolling average
price of each stock
@crichardson
Spring Integration + Complex event
processing (CEP) engine is a good
choice

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

@crichardson
Future[List[T]]
Not applicable to infinite streams

@crichardson
Introducing Reactive Extensions (Rx)
The Reactive Extensions (Rx) is a library for composing
asynchronous and event-based ...
About RxJava

Reactive Extensions (Rx) for the JVM
Implemented in Java
Adaptors for Scala, Groovy and Clojure

https://git...
RxJava core concepts
An asynchronous stream of
items
class Observable<T> {
Subscription subscribe(Observer<T> observer)
.....
Comparing Observable to...
Observer pattern - similar but adds
Observer.onComplete()
Observer.onError()
Iterator pattern -...
So what?

@crichardson
Transforming Observables
class Observable<T> {
Observable<T> take(int n);
Observable<T> skip(int n);
<R> Observable<R> map...
Transforming observables
Stream of streams!
class Observable<T> {
<K> Observable<GroupedObservable<K,T>>
groupBy(Func1<T,K...
Combining observables
class Observable<T> {
static <R,T1,T2>
Observable<R> zip(Observable<T0> o1,
Observable<T1> o2,
Func2...
Creating observables from data
class Observable<T> {
static Observable<T> from(Iterable<T> iterable]);
static Observable<T...
Creating observables from event sources
Observable<T> o = Observable.create(
new SubscriberFunc<T> () {
Subscription onSub...
Using Rx Observables instead of
Futures

@crichardson
Rx-based ProductInfoService
@Component
class ProductInfoService
override def getProductInfo(productId: Long) = {
val baseU...
ListenableFuture

Observable

implicit def toRxObservable[T](future: ListenableFuture[T])
: Observable[T] = {
def create(o...
Rx - ProductDetailsService

@Component
class ProductDetailsService @Autowired()
(productInfoService: ProductInfoService,
r...
Rx - ProductController
@Controller
class ProductController
@RequestMapping(Array("/productdetails/{productId}"))
@Response...
Rx - Observable

DeferredResult

implicit def toDeferredResult[T](observable : Observable[T]) = {
val result = new Deferre...
RxObservables vs. Futures

Much better than JDK 7 futures
Comparable to Scala Futures
Rx Scala code is slightly more verbo...
Making this code robust

Hystrix works with Observables (and thread pools)
But
For event-driven code we are on our own

@c...
Back to the stream of Trades averaging
example...

@crichardson
AMQP messages

Observables 1

<amqp:inbound-channel-adapter ... />
<int:channel id="inboundEventsChannel"/>
<int:service-a...
AMQP messages

Observables 2

class AmqpToObservableAdapter (actorRef : observerManager) {
def createObservable() =
Observ...
Calculating averages
Observable<AveragePrice>
calculateAverages(Observable<Trade> trades) {
...
}
class Trade {
private St...
Using groupBy()
Observable<Trade>

APPL : 401

IBM : 405

CAT : 405

APPL: 403

groupBy( (trade) => trade.symbol)

APPL : ...
Using window()
Observable<Trade>

APPL : 401

APPL : 405

APPL : 405

...

window(...)

M secs
APPL : 401

APPL : 405

APP...
Using reduce()
Observable<Trade>

APPL : 402

APPL : 405

APPL : 405

reduce(sumCalc) / length

APPL : 404

Observable<Ave...
Using merge()
Observable<Observable<AveragePrice>>

APPL : 401

APPL: 403
IBM : 405
CAT : 405

...

CAT : 405

APPL: 403

...
RxJS / NodeJS example
var rx = require("rx");
function tailFile (fileName) {
var self = this;
var o = rx.Observable.create...
Rx in the browser - implementing
completion
Change events
TextChanges(input)
.DistinctUntilChanged()
.Throttle(TimeSpan.Fr...
Summary
Consuming web services asynchronously is essential
Scala-style composable Futures are a powerful concurrency abstr...
@crichardson chris@chrisrichardson.net

Questions?

http://plainoldobjects.com

@crichardson
Upcoming SlideShare
Loading in...5
×

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

5,699

Published on

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.

0 Comments
20 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
5,699
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
98
Comments
0
Likes
20
Embeds 0
No embeds

No notes for slide

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

  1. 1. Consuming web services asynchronously with Futures and Rx Observables Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris@chrisrichardson.net http://plainoldobjects.com @crichardson
  2. 2. Presentation goal Learn how to use (Scala) Futures and Rx Observables to write simple yet robust and scalable concurrent code @crichardson
  3. 3. About Chris @crichardson
  4. 4. (About Chris) @crichardson
  5. 5. About Chris() @crichardson
  6. 6. About Chris @crichardson
  7. 7. About Chris http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/ @crichardson
  8. 8. About Chris @crichardson
  9. 9. Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions @crichardson
  10. 10. Let’s imagine you are building an online store @crichardson
  11. 11. Reviews Product Info Shipping Recomendations @crichardson
  12. 12. Product Info Reviews Sales ranking @crichardson
  13. 13. Related books Forum Viewing history @crichardson
  14. 14. + mobile apps @crichardson
  15. 15. Application architecture Desktop browser REST Product Info Service REST Recomendation Service REST Review Service Front end server HTML Mobile browser Native mobile client Web Application REST API gateway @crichardson
  16. 16. Handling getProductDetails() Product Info Service getProductInfo() getProductDetails() Front-end server getRecommendations() Recommendations Service getReviews() Review Service @crichardson
  17. 17. Handling getProductDetails() - sequentially Product Info Service getProductInfo() getProductDetails() Front-end server getRecommendations() Recommendations Service getReviews() Higher response time :-( Review Service @crichardson
  18. 18. Handling getProductDetails() - in parallel Product Info Service getProductInfo() getProductDetails() Front-end server getRecommendations() Recommendations Service getReviews() Lower response time :-) Review Service @crichardson
  19. 19. 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 @crichardson
  20. 20. The front-end server must handle partial failure of backend services Product Info Service getProductInfo() getProductDetails() Front-end server getRecommendations() getReviews() How to provide a good user experience? X Recommendations Service Review Service @crichardson
  21. 21. Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions @crichardson
  22. 22. Futures are a great concurrency abstraction http://en.wikipedia.org/wiki/Futures_and_promises @crichardson
  23. 23. How futures work initiates Asynchronous operation Client get set Outcome Future @crichardson
  24. 24. Benefits Client does not know how the asynchronous operation is implemented Client can invoke multiple asynchronous operations and gets a Future for each one. @crichardson
  25. 25. REST client using Spring @Async trait ProductInfoService { def getProductInfo(productId: Long): java.util.concurrent.Future[ProductInfo] } @Component class ProductInfoServiceImpl extends ProducInfoService { val restTemplate : RestTemplate = ... Execute asynchronously in thread pool @Async def getProductInfo(productId: Long) = { new AsyncResult(restTemplate.getForObject(....)...) } } A Future containing a value @crichardson
  26. 26. ProductDetailsService @Component class 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) } @crichardson
  27. 27. ProductController @Controller class ProductController @Autowired()(productDetailsService : ProductDetailsService) { @RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = productDetailsService.getProductDetails(productId) @crichardson
  28. 28. Not bad but... Blocks Tomcat thread until Future completes val productInfo = productInfoFuture.get(300, TimeUnit.MILLISECONDS) Not so scalable :-( @crichardson
  29. 29. ... 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 @crichardson
  30. 30. 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 ListenableFuture Java 8 CompletableFuture, Scala Futures @crichardson
  31. 31. Even better: Composable Futures val f1 = Future { val f2 = Future { ... ; 1 } ... ; 2 } Transforms Future val f4 = f2.map(_ * 2) assertEquals(4, Await.result(f4, 1 second)) val fzip = f1 zip f2 assertEquals((1, 2), Await.result(fzip, 1 second)) Combines two futures Scala, Java 8 CompletableFuture (partially) @crichardson
  32. 32. Scala futures are Monads def callB() : Future[...] = ... def callC() : Future[...] = ... def callD() : Future[...] = ... val result = for { (b, c) <- callB() zip callC(); d <- callD(b, c) } yield d Two calls execute in parallel And then invokes D result onSuccess { .... } Get the result of D @crichardson
  33. 33. Scala Future + RestTemplate import scala.concurrent.Future Scala Future @Component class ProductInfoService { def getProductInfo(productId: Long): Future[ProductInfo] = { Future { restTemplate.getForObject(....) } } } Executes in thread pool @crichardson
  34. 34. Scala Future + RestTemplate class 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! @crichardson
  35. 35. Async Spring MVC + Scala Futures @Controller class 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 @crichardson
  36. 36. Servlet layer is asynchronous but the backend uses thread pools Need event-driven REST client @crichardson
  37. 37. 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 @crichardson
  38. 38. Reactor pattern structure Initiation Dispatcher handle_events() register_handler(h) select(handlers) for each h in handlers h.handle_event(type) end loop handlers handle Event Handler handle_event(type) get_handle() notifies creates owns uses Synchronous Event Demultiplexer Application select() @crichardson
  39. 39. Java NIO Selectors = Reactor pattern But that’s super low-level :-( @crichardson
  40. 40. Spring AsyncRestTemplate New in Spring 4 Mirrors RestTemplate Methods return a ListenableFuture = JDK 7 Future + callback methods Can use HttpComponents NIO-based AsyncHttpClient @crichardson
  41. 41. Using the AsyncRestTemplate 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 http://hc.apache.org/httpcomponents-asyncclient-dev/ @crichardson
  42. 42. Converting ListenableFuture to Scala Future implicit def toScalaFuture[T](f : ListenableFuture[T]) : Future[T] = { val p = promise[T] The producer side of Scala Futures f.addCallback(new ListenableFutureCallback[T] { def onSuccess(result: T) { p.success(result)} def onFailure(t: Throwable) { p.failure(t) } }) p.future } Supply outcome Return future @crichardson
  43. 43. Now everything is non-blocking :-) @crichardson
  44. 44. 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 @crichardson
  45. 45. Fault tolerance at Netflix Network timeouts and retries Invoke remote services via a bounded thread pool Use the Circuit Breaker pattern On failure: return default/cached data return error to caller Implementation: https://github.com/Netflix/Hystrix http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html @crichardson
  46. 46. Using Hystrix @Component class ProductInfoServiceImpl extends ProductInfoService { val restTemplate = RestTemplateFactory.makeRestTemplate() val baseUrl = ... Runs in thread pool 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() } Returns JDK Future } @crichardson
  47. 47. But how to accomplish this with eventdriven code @crichardson
  48. 48. How to handling partial failures? Fails if any Future has failed productDetailsFuture zip recommendationsFuture zip reviewsFuture @crichardson
  49. 49. Handling partial failures val recommendationsFuture = recommendationService. getRecommendations(userId,productId). recover { case _ => Recommendations(List()) } “catch-like” Maps Throwable to value @crichardson
  50. 50. Implementing a Timeout No timeout - callbacks might never be invoked :-( resultFuture onSuccess { case r => result.setResult(r) } resultFuture onFailure { case t => result.setErrorResult(t) } Await.result(resultFuture, timeout) Blocks until timeout:-( @crichardson
  51. 51. Non-blocking Timeout val future = ... val timedoutFuture = TimeoutFuture(future)(executionContext, 200.milleseconds) 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() }) } } Timer fails promise Outcome of first completed http://eng.42go.com/future-safefuture-timeout-cancelable/ @crichardson
  52. 52. 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() } @crichardson
  53. 53. Limiting # of simultaneous requests val 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] @crichardson
  54. 54. Putting it all together @Component class ProductInfoServiceImpl @Autowired()(...) extends ProductService { val limiter = new ConcurrencyLimiter(...) val breaker = new CircuitBreaker(...) override def getProductInfo(productId: Long) = { ... breaker.withCircuitBreaker { limiter.withLimit { TimeoutFuture { ... AsyncRestTemplate.get ... } } } } @crichardson
  55. 55. Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions @crichardson
  56. 56. @crichardson
  57. 57. Why solve this problem for JavaScript? Browser invokes web services Implement front-end server/API gateway using NodeJS! @crichardson
  58. 58. What’s NodeJS? Designed for DIRTy apps @crichardson
  59. 59. JavaScript NodeJS Modules Reactor pattern @crichardson
  60. 60. Asynchronous JavaScript code = callback hell Scenarios: Sequential: A B C Fork and join: A and B C Code quickly becomes very messy @crichardson
  61. 61. Callback-based HTTP client request = require("request") handler = (error, clientResponse, body) -> if clientResponse.statusCode != 200 // ERROR else // SUCCESS request("http://.../productdetails/" + productId, handler) @crichardson
  62. 62. Messy callback code getProductDetails = (req, res) -> productId = req.params.productId result = {productId: productId} Returns a callback function 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) Have all three requests completed? 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) @crichardson
  63. 63. 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 @crichardson
  64. 64. Basic promise API called when promise1 is fulfilled returns a promise or value promise2 = promise1.then(fulfilledHandler, errorHandler, ...) resolved with outcome of callback called when promise1 is rejected returns a promise or value @crichardson
  65. 65. About when.js Rich 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
  66. 66. Simpler promise-based code rest = require("rest") @httpClient = rest.chain(mime).chain(errorCode); No ugly callbacks getProductInfo : (productId) -> @httpClient path: "http://.../productinfo/" + productId Returns a promise @crichardson
  67. 67. Simpler promise-based code class ProductDetailsService getProductDetails: (productId) -> makeProductDetails = (productInfo, recommendations, reviews) -> details = productId: productId Array[Promise] productDetails: productInfo.entity recommendations: recommendations.entity reviews: reviews.entity details responses = [@getProductInfo(productId), @getRecommendations(productId), @getReviews(productId)] all(responses).spread(makeProductDetails) Promise[Array] @crichardson
  68. 68. 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) @crichardson
  69. 69. Writing robust client code @crichardson
  70. 70. Implementing timeouts Creates a new promise Original promise must complete within 300 msec timeout = require("when/timeout") withTimeout = (promise) -> timeout(300, promise) getProductDetails = (productId) -> ... withTimeout(client(...)) @crichardson
  71. 71. Recovering from failures Invoked to return default value if getRecommendations() fails getRecommendations(productId).otherwise( -> {}) @crichardson
  72. 72. Limiting # of concurrent requests ConcurrencyLimiter = require("./concurrencylimiter").ConcurrencyLimiter limiter = new ConcurrencyLimiter(maxQueueSize = 100, maxConcurrency = 10) getProductDetails = (productId) -> ... limiter.withLimit( -> client(...)) Homegrown code @crichardson
  73. 73. Circuit breaker CircuitBreaker = require("./circuitbreaker").CircuitBreaker productCircuitBreaker = new CircuitBreaker() getProductDetails = (productId) -> ... productCircuitBreaker.withCircuitBreaker( -> client(...)) Homegrown code @crichardson
  74. 74. Putting it all together getProductInfo = (productId) -> limiter.withLimit -> productCircuitBreaker.withCircuitBreaker -> withTimeout client path: "http://..../productinfo/" + productd getProductDetails: (productId) -> ... responses = [@getProductInfo(productId), @getRecommendations(productId).otherwise(() -> {}), @getReviews(productId).otherwise(() -> {})] all(responses).spread(succeed) @crichardson
  75. 75. Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions @crichardson
  76. 76. Let’s imagine you have a stream of trades and you need to calculate the rolling average price of each stock @crichardson
  77. 77. Spring Integration + Complex event processing (CEP) engine is a good choice @crichardson
  78. 78. But where is the high-level abstraction that solves this problem? @crichardson
  79. 79. Future[List[T]] Not applicable to infinite streams @crichardson
  80. 80. 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/ @crichardson
  81. 81. About RxJava Reactive Extensions (Rx) for the JVM Implemented in Java Adaptors for Scala, Groovy and Clojure https://github.com/Netflix/RxJava @crichardson
  82. 82. RxJava core concepts An asynchronous stream of items class Observable<T> { Subscription subscribe(Observer<T> observer) ... } Used to unsubscribe Notifies interface Observer<T> { void onNext(T args) void onCompleted() void onError(Throwable e) } @crichardson
  83. 83. 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 @crichardson
  84. 84. So what? @crichardson
  85. 85. 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 @crichardson
  86. 86. Transforming observables Stream of streams! class Observable<T> { <K> Observable<GroupedObservable<K,T>> groupBy(Func1<T,K> keySelector) ... } Similar to Scala groupBy except results are emitted as items arrive @crichardson
  87. 87. Combining observables class Observable<T> { static <R,T1,T2> Observable<R> zip(Observable<T0> o1, Observable<T1> o2, Func2<T1,T2,R> reduceFunction) ... } Stream of results from reduceFunction Invoked with pairs of items from o1 and o2 @crichardson
  88. 88. 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]); ... } @crichardson
  89. 89. Creating observables from event sources Observable<T> o = Observable.create( new SubscriberFunc<T> () { Subscription onSubscribe(Observer<T> obs) { ... connect to event source.... Called once for each Observer Arranges to call obs.onNext/onComplete/... return new Subscriber () { void unsubscribe() { ... }; }; }); Called when unsubscribing @crichardson
  90. 90. Using Rx Observables instead of Futures @crichardson
  91. 91. Rx-based ProductInfoService @Component class 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 } } @crichardson
  92. 92. 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() {} } } Supply single value Indicate failure Do nothing Observable.create(create _) } @crichardson
  93. 93. Rx - ProductDetailsService @Component class 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) Just like Scala Futures Observable.zip(productInfo, recommendations, reviews, (p : ProductInfo, r : Recommendations, rv : Reviews) => ProductDetails(p, r, rv)) } } @crichardson
  94. 94. Rx - ProductController @Controller class ProductController @RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = toDeferredResult(productDetailsService.getProductDetails(productId)) @crichardson
  95. 95. 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 } @crichardson
  96. 96. RxObservables vs. Futures Much better than JDK 7 futures Comparable to Scala Futures Rx Scala code is slightly more verbose @crichardson
  97. 97. Making this code robust Hystrix works with Observables (and thread pools) But For event-driven code we are on our own @crichardson
  98. 98. Back to the stream of Trades averaging example... @crichardson
  99. 99. AMQP messages Observables 1 <amqp:inbound-channel-adapter ... /> <int:channel id="inboundEventsChannel"/> <int:service-activator input-channel="inboundEventsChannel" ref="amqpToObservableAdapter" method="handleMessage"/> @crichardson
  100. 100. AMQP messages Observables 2 class AmqpToObservableAdapter (actorRef : observerManager) { def createObservable() = Observable.create((o: Observer[_ >: String]) observerManager ! Subscribe(o) new Subscription { override def unsubscribe() { observerManager ! Unsubscribe(o) } } => { Manages and notifies observers }) def handleMessage(message : String) { observerManager ! Message(message) } } @crichardson
  101. 101. Calculating averages Observable<AveragePrice> calculateAverages(Observable<Trade> trades) { ... } class Trade { private String symbol; private double price; class AveragePrice { private String symbol; private double averagePrice; @crichardson
  102. 102. Using groupBy() Observable<Trade> APPL : 401 IBM : 405 CAT : 405 APPL: 403 groupBy( (trade) => trade.symbol) APPL : 401 APPL: 403 IBM : 405 CAT : 405 Observable<GroupedObservable<String, Trade>> ... @crichardson
  103. 103. Using window() Observable<Trade> APPL : 401 APPL : 405 APPL : 405 ... window(...) M secs APPL : 401 APPL : 405 APPL : 405 APPL : 405 APPL : 403 N secs N secs APPL : 405 APPL : 405 ... Observable<Observable<Trade>> @crichardson
  104. 104. Using reduce() Observable<Trade> APPL : 402 APPL : 405 APPL : 405 reduce(sumCalc) / length APPL : 404 Observable<AveragePrice> Singleton @crichardson
  105. 105. Using merge() Observable<Observable<AveragePrice>> APPL : 401 APPL: 403 IBM : 405 CAT : 405 ... CAT : 405 APPL: 403 merge() APPL : 401 Observable<AveragePrice> IBM : 405 @crichardson
  106. 106. RxJS / NodeJS example var 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) { ... } @crichardson
  107. 107. Rx in the browser - implementing completion Change events TextChanges(input) .DistinctUntilChanged() .Throttle(TimeSpan.FromMilliSeconds(10)) .Select(word=>Completions(word)) .Switch() .Subscribe(ObserveChanges(output)); Observable Ajax call returning Observable Display completions Your Mouse is a Database http://queue.acm.org/detail.cfm?id=2169076 @crichardson
  108. 108. 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 @crichardson
  109. 109. @crichardson chris@chrisrichardson.net Questions? http://plainoldobjects.com @crichardson
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×