Your SlideShare is downloading. ×
0
@crichardson
Consuming web services
asynchronously with Futures
and Rx Observables
Chris Richardson
Author of POJOs in Act...
@crichardson
Presentation goal
Learn how to use (Scala)
Futures and Rx
Observables to write simple
yet robust and scalable...
@crichardson
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...
@crichardson
Let’s imagine you are building
an online store
@crichardson
Shipping
Recomendations
Product
Info
Reviews
@crichardson
Reviews
Product
Info Sales
ranking
@crichardson
Related
books
Viewing
history
Forum
@crichardson
+ mobile apps
@crichardson
Application architecture
Product Info Service
Desktop
browser
Native
mobile
client
REST
REST
REST
Recomendati...
@crichardson
Handling getProductDetails()
Front-end
server
Product Info Service
Recommendations
Service
Review
Service
get...
@crichardson
Handling getProductDetails() -
sequentially
Front-end
server
Product Info Service
Recommendations
Service
Rev...
@crichardson
Handling getProductDetails() -
in parallel
Front-end
server
Product Info Service
Recommendations
Service
Revi...
@crichardson
Implementing a concurrent
REST client
Thread-pool based approach
executorService.submit(new Callable(...))
Si...
@crichardson
The front-end server must handle
partial failure of backend services
Front-end
server
Product Info Service
Re...
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript...
@crichardson
Futures are a great
concurrency abstraction
http://en.wikipedia.org/wiki/Futures_and_promises
@crichardson
Worker threadMain thread
How futures work
Outcome
Future
Client
get
Asynchronous
operation
set
initiates
@crichardson
Benefits
Client does not know how the asynchronous operation is
implemented
Client can invoke multiple asynchr...
@crichardson
Handling ProductDetails request
ProductDetailsController
ProductDetailsService
getProductDetails()
ProductInf...
@crichardson
REST client using Spring @Async
@Component
class ProductInfoServiceImpl extends ProducInfoService {
val restT...
@crichardson
ProductDetailsService
@Component
class ProductDetailsService
@Autowired()(productInfoService: ProductInfoServ...
@crichardson
ProductController
@Controller
class ProductController
@Autowired()(productDetailsService : ProductDetailsServ...
@crichardson
Blocks Tomcat thread until
Future completes
Not bad but...
class ProductDetailsService
def getProductDetails(...
@crichardson
... and also...
Java Futures work well for a single-level of asynchronous
execution
BUT
#fail for more comple...
@crichardson
Better: Futures with callbacks
no blocking!
val f : Future[Int] = Future { ... }
f onSuccess {
case x : Int =...
@crichardson
Even better: composable futures
def asyncOperation() = Future { ... ; 1 }
val r = asyncOperation().map{ _ * 2...
@crichardson
map() is asynchronous - uses callbacks
class Future[T] {
def map[S](f: T => S): Future[S] = {
val p = Promise...
@crichardson
Chaining using flatmap()
def asyncOperation() = Future { ... ; 3 }
def asyncSquare(x : Int) : Future[Int] = Fu...
@crichardson
Combining futures
val f1 = Future { ... ; 1}
val f2 = Future { .... ; 2}
val fzip = f1 zip f2
assertEquals((1...
@crichardson
Scala futures are Monads
def callB() : Future[...] = ...
def callC() : Future[...] = ...
def callD() : Future...
@crichardson
‘for’ is shorthand for map() and flatMap()
(callB() zip callC()) flatMap { r1 =>
val (b, c) = r1
callD(b, c) m...
@crichardson
Scala Future + RestTemplate
import scala.concurrent.Future
@Component
class ProductInfoService {
def getProdu...
@crichardson
Scala Future + RestTemplate
class ProductDetailsService @Autowired()(....) {
def getProductDetails(productId:...
@crichardson
Async Spring MVC + Scala
Futures
@Controller
class ProductController ... {
@RequestMapping(Array("/productdet...
@crichardson
Servlet layer is asynchronous
but the backend uses thread
pools
Need event-driven REST
client
@crichardson
New in Spring 4
Mirrors RestTemplate
Methods return a ListenableFuture
ListenableFuture = JDK 7 Future + call...
@crichardson
Using the AsyncRestTemplate
http://hc.apache.org/httpcomponents-asyncclient-dev/
val asyncRestTemplate = new ...
@crichardson
Converting ListenableFuture to
Scala Future
implicit def toScalaFuture[T](f :
ListenableFuture[T]) : Future[T...
@crichardson
Now everything is non-
blocking :-)
@crichardson
If recommendation service is
down...
Never responds front-end server waits indefinitely
Consumes valuable fron...
@crichardson
Fault tolerance at Netflix
Network timeouts and retries
Invoke remote services via a bounded thread pool
Use t...
@crichardson
How to handling partial
failures?
productDetailsFuture zip
recommendationsFuture zip
reviewsFuture
Fails if a...
@crichardson
Handling partial failures
recommendationService.
getRecommendations(userId,productId)
.recover {
case _ => Re...
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript...
@crichardson
Why solve this problem for
JavaScript?
Browser invokes web services
Implement front-end server/API gateway us...
@crichardson
What’s NodeJS?
Designed for DIRTy apps
@crichardson
NodeJS
JavaScript
Reactor
pattern
Modules
@crichardson
Asynchronous JavaScript
code = callback hell
Scenarios:
Sequential: A B C
Fork and join: A and B C
Code quick...
@crichardson
Callback-based HTTP client
request = require("request")
handler = (error, clientResponse, body) ->
if clientR...
@crichardson
Messy callback code
getProductDetails = (req, res) ->
productId = req.params.productId
result = {productId: p...
@crichardson
Simplifying code with
Promises (a.k.a. Futures)
Functions return a promise - no callback parameter
A promise ...
@crichardson
Basic promise API
promise2 = promise1.then(fulfilledHandler, errorHandler, ...)
called when promise1 is fulfil...
About when.js
Rich API = Promises spec
+ use full extensions
Creation:
when.defer()
when.reject()...
Combinators:
all, som...
@crichardson
Simpler promise-based code
rest = require("rest")
@httpClient = rest.chain(mime).chain(errorCode);
getProduct...
@crichardson
Simpler promise-based code
class ProductDetailsService
getProductDetails: (productId) ->
makeProductDetails =...
@crichardson
Simpler promise-based code:
HTTP handler
getProductDetails = (req, res) ->
productId = req.params.productId
s...
@crichardson
Writing robust client code
@crichardson
Recovering from failures
getRecommendations(productId)
.otherwise( -> {})
Invoked to return default value if
...
@crichardson
Agenda
The need for concurrency
Simplifying concurrent code with Futures
Taming callback hell with JavaScript...
@crichardson
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...
@crichardson
About RxJava
Reactive Extensions (Rx) for the JVM
Implemented in Java
Adaptors for Scala, Groovy and Clojure
...
@crichardson
RxJava core concepts
class Observable<T> {
Subscription subscribe(Observer<T> observer)
...
}
interface Obser...
@crichardson
Comparing Observable to...
Observer pattern - similar but adds
Observer.onComplete()
Observer.onError()
Itera...
@crichardson
So what?
@crichardson
Transforming Observables
class Observable<T> {
Observable<T> take(int n);
Observable<T> skip(int n);
<R> Obse...
@crichardson
Transforming observables
class Observable<T> {
<K> Observable<GroupedObservable<K,T>>
groupBy(Func1<T,K> keyS...
@crichardson
Combining observables
class Observable<T> {
static <R,T1,T2>
Observable<R> zip(Observable<T0> o1,
Observable<...
@crichardson
Creating observables from
data
class Observable<T> {
static Observable<T> from(Iterable<T> iterable]);
static...
@crichardson
Arranges to call obs.onNext/
onComplete/...
Creating observables from
event sources
Observable<T> o = Observa...
@crichardson
Using Rx Observables
instead of Futures
@crichardson
Rx-based ProductInfoService
@Component
class ProductInfoService
override def getProductInfo(productId: Long) ...
@crichardson
ListenableFuture Observable
implicit def toRxObservable[T](future: ListenableFuture[T])
: Observable[T] = {
d...
@crichardson
Rx - ProductDetailsService
@Component
class ProductDetailsService @Autowired()
(productInfoService: ProductIn...
@crichardson
Rx - ProductController
@Controller
class ProductController
@RequestMapping(Array("/productdetails/{productId}...
@crichardson
Rx - Observable
DeferredResult
implicit def toDeferredResult[T](observable : Observable[T]) = {
val result = ...
@crichardson
RxObservables vs. Futures
Much better than JDK 7 futures
Comparable to Scala Futures
Rx Scala code is slightl...
@crichardson
Making this code robust
Hystrix works with Observables (and thread pools)
But
For event-driven code we are on...
@crichardson
Back to the stream of Trades
averaging example...
@crichardson
Calculating averages
Observable<AveragePrice>
calculateAverages(Observable<Trade> trades) {
...
}
class Trade...
@crichardson
Using groupBy()
APPL : 401 IBM : 405 CAT : 405 APPL: 403
groupBy( (trade) => trade.symbol)
APPL : 401
IBM : 4...
@crichardson
Using window()
APPL : 401 APPL : 405 APPL : 405 ...
APPL : 401 APPL : 405 APPL : 405
APPL : 405 APPL : 405 AP...
@crichardson
Using fold()
APPL : 402 APPL : 405 APPL : 405
APPL : 404
fold(0)(sumCalc) / length
Observable<Trade>
Observab...
@crichardson
Using merge()
merge()
APPL : 401
IBM : 405
CAT : 405 ...
APPL: 403
APPL : 401 IBM : 405 CAT : 405 APPL: 403
O...
@crichardson
Calculating average prices
def calculateAverages(trades: Observable[Trade]): Observable[AveragePrice] = {
tra...
@crichardson
RxJS / NodeJS example
var rx = require("rx");
function tailFile (fileName) {
var self = this;
var o = rx.Obse...
@crichardson
Rx in the browser -
implementing completion
TextChanges(input)
.DistinctUntilChanged()
.Throttle(TimeSpan.Fro...
@crichardson
Summary
Consuming web services asynchronously is essential
Scala-style are a powerful concurrency abstraction...
@crichardson
Questions?
@crichardson chris@chrisrichardson.net
http://plainoldobjects.com
Upcoming SlideShare
Loading in...5
×

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

3,109

Published on

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.

Published in: Technology, Business
0 Comments
13 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,109
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
89
Comments
0
Likes
13
Embeds 0
No embeds

No notes for slide

Transcript of "Consuming web services asynchronously with Futures and Rx Observables (svcc, svcc2013)"

  1. 1. @crichardson 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
  2. 2. @crichardson Presentation goal Learn how to use (Scala) Futures and Rx Observables to write simple yet robust and scalable concurrent code
  3. 3. @crichardson About Chris
  4. 4. @crichardson (About Chris)
  5. 5. @crichardson About Chris()
  6. 6. @crichardson About Chris
  7. 7. @crichardson About Chris http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/
  8. 8. @crichardson About Chris
  9. 9. @crichardson Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions
  10. 10. @crichardson Let’s imagine you are building an online store
  11. 11. @crichardson Shipping Recomendations Product Info Reviews
  12. 12. @crichardson Reviews Product Info Sales ranking
  13. 13. @crichardson Related books Viewing history Forum
  14. 14. @crichardson + mobile apps
  15. 15. @crichardson Application architecture Product Info Service Desktop browser Native mobile client REST REST REST Recomendation Service Review Service Front end server API gatewayREST Mobile browser Web Application HTML/ JSON JSON
  16. 16. @crichardson Handling getProductDetails() Front-end server Product Info Service Recommendations Service Review Service getProductInfo() getRecommendations() getReviews() getProductDetails()
  17. 17. @crichardson Handling getProductDetails() - sequentially Front-end server Product Info Service Recommendations Service Review Service getProductInfo() getRecommendations() getReviews() getProductDetails() Higher response time :-(
  18. 18. @crichardson Handling getProductDetails() - in parallel Front-end server Product Info Service Recommendations Service Review Service getProductInfo() getRecommendations() getReviews() getProductDetails() Lower response time :-)
  19. 19. @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
  20. 20. @crichardson The front-end server must handle partial failure of backend services Front-end server Product Info Service Recommendations Service Review Service getProductInfo() getRecommendations() getReviews() getProductDetails() X How to provide a good user experience?
  21. 21. @crichardson Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions
  22. 22. @crichardson Futures are a great concurrency abstraction http://en.wikipedia.org/wiki/Futures_and_promises
  23. 23. @crichardson Worker threadMain thread How futures work Outcome Future Client get Asynchronous operation set initiates
  24. 24. @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.
  25. 25. @crichardson Handling ProductDetails request ProductDetailsController ProductDetailsService getProductDetails() ProductInfoService getProductInfo() ReviewService getReviews() RecommendationService getRecommendations() RestTemplate
  26. 26. @crichardson REST client using Spring @Async @Component class ProductInfoServiceImpl extends ProducInfoService { val restTemplate : RestTemplate = ... @Async def getProductInfo(productId: Long) = { new AsyncResult(restTemplate.getForObject(....)...) } } Execute asynchronously in thread pool A fulfilled Future trait ProductInfoService { def getProductInfo(productId: Long): java.util.concurrent.Future[ProductInfo] }
  27. 27. @crichardson 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(pr 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)
  28. 28. @crichardson ProductController @Controller class ProductController @Autowired()(productDetailsService : ProductDetailsService) { @RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = productDetailsService.getProductDetails(productId)
  29. 29. @crichardson Blocks Tomcat thread until Future completes Not bad but... class ProductDetailsService def getProductDetails(productId: Long): ProductDetails = { val productInfo = productInfoFuture.get(300, TimeUnit.MILLISECONDS) Not so scalable :-(
  30. 30. @crichardson ... and also... Java Futures work well for a single-level of asynchronous execution BUT #fail for more complex, scalable scenarios Difficult to compose and coordinate multiple concurrent operations See this blog post for more details: http://techblog.netflix.com/2013/02/rxjava-netflix-api.html
  31. 31. @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 ListenableFuture Java 8 CompletableFuture, Scala Futures
  32. 32. @crichardson Even better: composable futures def asyncOperation() = Future { ... ; 1 } val r = asyncOperation().map{ _ * 2 } assertEquals(4, Await.result(r, 1 second)) Scala, Java 8 CompletableFuture (partially) Transforms Future
  33. 33. @crichardson map() is asynchronous - uses callbacks class Future[T] { def map[S](f: T => S): Future[S] = { val p = Promise[S]() onSuccess { case r => p success f(r) } onFailure { case t => p failure t } p.future } When ‘this’ completes propagate outcome to ‘p’ Return new Future
  34. 34. @crichardson Chaining using flatmap() def asyncOperation() = Future { ... ; 3 } def asyncSquare(x : Int) : Future[Int] = Future { x * x} val r = asyncOperation().flatMap { x => asyncSquare(x) } assertEquals(9, Await.result(r, 1 second)) Scala, Java 8 CompletableFuture (partially) Chaining asynchronous operations
  35. 35. @crichardson Combining futures val f1 = Future { ... ; 1} val f2 = Future { .... ; 2} val fzip = f1 zip f2 assertEquals((1, 2), Await.result(fzip, 1 second)) def asyncOp(x : Int) = Future { x * x} val f = Future.sequence((1 to 5).map { x => asyncOp(x) }) assertEquals(List(1, 4, 9, 16, 25), Await.result(f, 1 second)) Scala, Java 8 CompletableFuture (partially) Combines two futures Transforms list of futures to a future containing a list
  36. 36. @crichardson 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 result onSuccess { .... } Two calls execute in parallel And then invokes D Get the result of D
  37. 37. @crichardson ‘for’ is shorthand for map() and flatMap() (callB() zip callC()) flatMap { r1 => val (b, c) = r1 callD(b, c) map { d => d } } for { (b, c) <- callB() zip callC(); d <- callD(b, c) } yield d
  38. 38. @crichardson Scala Future + RestTemplate import scala.concurrent.Future @Component class ProductInfoService { def getProductInfo(productId: Long): Future[ProductInfo] = { Future { restTemplate.getForObject(....) } } } Executes in thread pool Scala Future
  39. 39. @crichardson 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!
  40. 40. @crichardson 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
  41. 41. @crichardson Servlet layer is asynchronous but the backend uses thread pools Need event-driven REST client
  42. 42. @crichardson New in Spring 4 Mirrors RestTemplate Methods return a ListenableFuture ListenableFuture = JDK 7 Future + callback methods Can use HttpComponents NIO-based AsyncHttpClient Spring AsyncRestTemplate
  43. 43. @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
  44. 44. @crichardson Converting ListenableFuture to Scala Future implicit def toScalaFuture[T](f : ListenableFuture[T]) : Future[T] = { val p = promise[T]() f.addCallback(new ListenableFutureCallback[T] { def onSuccess(result: T) { p.success(result)} def onFailure(t: Throwable) { p.failure(t) } }) p.future } The producer side of Scala Futures Supply outcome Return future
  45. 45. @crichardson Now everything is non- blocking :-)
  46. 46. @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
  47. 47. @crichardson 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
  48. 48. @crichardson How to handling partial failures? productDetailsFuture zip recommendationsFuture zip reviewsFuture Fails if any Future has failed
  49. 49. @crichardson Handling partial failures recommendationService. getRecommendations(userId,productId) .recover { case _ => Recommendations(List()) } “catch-like” Maps Throwable to value
  50. 50. @crichardson Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions
  51. 51. @crichardson Why solve this problem for JavaScript? Browser invokes web services Implement front-end server/API gateway using NodeJS
  52. 52. @crichardson What’s NodeJS? Designed for DIRTy apps
  53. 53. @crichardson NodeJS JavaScript Reactor pattern Modules
  54. 54. @crichardson Asynchronous JavaScript code = callback hell Scenarios: Sequential: A B C Fork and join: A and B C Code quickly becomes very messy
  55. 55. @crichardson Callback-based HTTP client request = require("request") handler = (error, clientResponse, body) -> if clientResponse.statusCode != 200 // ERROR else // SUCCESS request("http://.../productdetails/" + productId, handler)
  56. 56. @crichardson Messy callback code getProductDetails = (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?
  57. 57. @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
  58. 58. @crichardson Basic promise API promise2 = promise1.then(fulfilledHandler, errorHandler, ...) called when promise1 is fulfilled returns a promise or value resolved with outcome of callback called when promise1 is rejected
  59. 59. 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
  60. 60. @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
  61. 61. @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]
  62. 62. @crichardson Simpler promise-based code: HTTP handler getProductDetails = (req, res) -> productId = req.params.productId succeed = (productDetails) -> res.json(productDetails) fail = (something) -> res.send(500, JSON.stringify(something.entity || something)) productDetailsService. getDetails(productId). then(succeed, fail) app.get('/productdetails/:productId', getProductDetails)
  63. 63. @crichardson Writing robust client code
  64. 64. @crichardson Recovering from failures getRecommendations(productId) .otherwise( -> {}) Invoked to return default value if getRecommendations() fails
  65. 65. @crichardson Agenda The need for concurrency Simplifying concurrent code with Futures Taming callback hell with JavaScript promises Consuming asynchronous streams with Reactive Extensions
  66. 66. @crichardson Let’s imagine you have a stream of trades and you need to calculate the rolling average price of each stock
  67. 67. @crichardson Spring Integration + Complex event processing (CEP) engine is a good choice
  68. 68. @crichardson But where is the high-level abstraction that solves this problem?
  69. 69. @crichardson Future[List[T]] Not applicable to infinite streams
  70. 70. @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/
  71. 71. @crichardson About RxJava Reactive Extensions (Rx) for the JVM Implemented in Java Adaptors for Scala, Groovy and Clojure https://github.com/Netflix/RxJava
  72. 72. @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
  73. 73. @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
  74. 74. @crichardson So what?
  75. 75. @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
  76. 76. @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!
  77. 77. @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
  78. 78. @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]); ... }
  79. 79. @crichardson Arranges to call obs.onNext/ onComplete/... Creating observables from event sources Observable<T> o = Observable.create( new SubscriberFunc<T> () { Subscription onSubscribe(Observer<T> obs) { ... connect to event source.... return new Subscriber () { void unsubscribe() { ... }; }; }); Called once for each Observer Called when unsubscribing
  80. 80. @crichardson Using Rx Observables instead of Futures
  81. 81. @crichardson 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 } }
  82. 82. @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
  83. 83. @crichardson 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) Observable.zip(productInfo, recommendations, reviews, (p : ProductInfo, r : Recommendations, rv : Reviews) => ProductDetails(p, r, rv)) } } Just like Scala Futures
  84. 84. @crichardson Rx - ProductController @Controller class ProductController @RequestMapping(Array("/productdetails/{productId}")) @ResponseBody def productDetails(@PathVariable productId: Long) = val pd = productDetailsService.getProductDetails(productId) toDeferredResult(pd)
  85. 85. @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 }
  86. 86. @crichardson RxObservables vs. Futures Much better than JDK 7 futures Comparable to Scala Futures Rx Scala code is slightly more verbose
  87. 87. @crichardson Making this code robust Hystrix works with Observables (and thread pools) But For event-driven code we are on our own
  88. 88. @crichardson Back to the stream of Trades averaging example...
  89. 89. @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;
  90. 90. @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>
  91. 91. @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
  92. 92. @crichardson Using fold() APPL : 402 APPL : 405 APPL : 405 APPL : 404 fold(0)(sumCalc) / length Observable<Trade> Observable<AveragePrice> Singleton
  93. 93. @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>
  94. 94. @crichardson Calculating average prices def calculateAverages(trades: Observable[Trade]): Observable[AveragePrice] = { trades.groupBy(_.symbol).map { symbolAndTrades => val (symbol, tradesForSymbol) = symbolAndTrades ... tradesForSymbol.window(...).map { windowOfTradesForSymbol => windowOfTradesForSymbol.fold((0.0, 0, List[Double]())) { (soFar, trade) => val (sum, count, prices) = soFar (sum + trade.price, count + 1, trade.price +: prices) } map { x => val (sum, length, prices) = x AveragePrice(symbol, sum / length, prices) } }.flatMap(identity) }.flatMap(identity) }
  95. 95. @crichardson 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) { ... }
  96. 96. @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 Database http://queue.acm.org/detail.cfm?id=2169076 Ajax call returning Observable Change events Observable Display completions
  97. 97. @crichardson Summary Consuming web services asynchronously is essential Scala-style are a powerful concurrency abstraction Rx Observables are even more powerful Rx Observables are a unifying abstraction for a wide variety of use cases
  98. 98. @crichardson Questions? @crichardson chris@chrisrichardson.net http://plainoldobjects.com
  1. A particular slide catching your eye?

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

×