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

11,038 views

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.

1 Comment
27 Likes
Statistics
Notes
  • for JavaScript you can use reactive bacon.js!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
11,038
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
135
Comments
1
Likes
27
Embeds 0
No embeds

No notes for slide

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

×