reactive programming with rx
play

Reactive Programming with Rx QConSF - November 2014 Ben Christensen - PowerPoint PPT Presentation

Reactive Programming with Rx QConSF - November 2014 Ben Christensen Developer Edge Engineering at Netflix @benjchristensen http://techblog.netflix.com/ RxJava http://github.com/ReactiveX/RxJava http://reactivex.io Maven Central:


  1. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  2. flatMap ¡Observable<R> ¡b ¡= ¡Observable<T> . flatMap ({ ¡T ¡t ¡ -­‑> ¡ ¡ ¡ ¡ ¡ ¡Observable<R> ¡r ¡= ¡... ¡transform ¡t ¡... ¡ ¡ ¡ ¡ ¡ return ¡r; ¡ ¡ })

  3. flatMap ¡Observable<R> ¡b ¡= ¡Observable<T> . flatMap ({ ¡T ¡t ¡ -­‑> ¡ ¡ ¡ ¡ ¡ ¡Observable<R> ¡r ¡= ¡... ¡transform ¡t ¡... ¡ ¡ ¡ ¡ ¡ return ¡r; ¡ ¡ })

  4. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  5. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  6. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  7. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  8. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  9. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  10. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  11. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  12. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  13. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  14. zip { ( , ) } ¡ ¡ ¡ ¡Observable . zip ( a, ¡b, ¡ ( a, ¡b) ¡ -­‑> ¡{ ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡... ¡operate ¡on ¡values ¡from ¡both ¡a ¡& ¡b ¡... ¡ ¡ ¡ ¡ ¡ ¡ ¡ return ¡Arrays.asList(a, ¡b); ¡ ¡ ¡ ¡ ¡ })

  15. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  16. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  17. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  18. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  19. instead of a blocking api ... class ¡ VideoService ¡ { ¡ ¡ ¡ ¡ def ¡VideoList ¡getPersonalizedListOfMovies ( userId ); ¡ ¡ ¡ ¡def ¡VideoBookmark ¡getBookmark ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡VideoRating ¡getRating ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡VideoMetadata ¡getMetadata ( videoId ); ¡ } ... create an observable api: class ¡ VideoService ¡ { ¡ ¡ ¡ ¡ def ¡Observable < VideoList > ¡getPersonalizedListOfMovies ( userId ); ¡ ¡ ¡ ¡def ¡Observable < VideoBookmark > ¡getBookmark ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoRating > ¡getRating ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoMetadata > ¡getMetadata ( videoId ); ¡ }

  20. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { 1 // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) 2 .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); 3 Observable<Rating> rating = getRating(video); 4 Observable<VideoMetadata> metadata = getMetadata(video); 5 return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { 6 return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  21. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { 1 // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) 2 .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); 3 Observable<Rating> rating = getRating(video); 4 Observable<VideoMetadata> metadata = getMetadata(video); 5 return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { 6 return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  22. Non-Opinionated Concurrency

  23. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  24. public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) { // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); }).flatMap(data -> { // output as SSE as we get back the data (no waiting until all is done) return response.writeAndFlush(new ServerSentEvent(SimpleJson.mapToJson(data))); }); }

  25. Decouples Consumption from Production // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); })

  26. Decouples Consumption from Production // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); })

  27. Decouples Consumption from Production // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); Observable<Rating> rating = getRating(video); Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); })

  28. Decouples Consumption from Production API Request new Command().observe() API Request new Command(). observe() Collapser Dependency API Request new Command(). observe() Event Loop REST API API Request new Command(). observe() API Request new Command(). observe()

  29. ~5 network calls (#3 and #4 may result in more due to windowing) // first request User object return getUser(request.getQueryParameters().get("userId")).flatMap(user -> { 1 // then fetch personal catalog Observable<Map<String, Object>> catalog = getPersonalizedCatalog(user) 2 .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = getBookmark(video); 3 Observable<Rating> rating = getRating(video); 4 Observable<VideoMetadata> metadata = getMetadata(video); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); // and fetch social data in parallel Observable<Map<String, Object>> social = getSocialData(user).map(s -> { 5 return s.getDataAsMap(); }); // merge the results return Observable.merge(catalog, social); })

  30. Clear API Communicates Potential Cost class ¡ VideoService ¡ { ¡ ¡ ¡ ¡ def ¡Observable < VideoList > ¡getPersonalizedListOfMovies ( userId ); ¡ ¡ ¡ ¡def ¡Observable < VideoBookmark > ¡getBookmark ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoRating > ¡getRating ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoMetadata > ¡getMetadata ( videoId ); ¡ }

  31. Implementation Can Differ BIO Network Call class ¡ VideoService ¡ { ¡ ¡ ¡ ¡ def ¡Observable < VideoList > ¡getPersonalizedListOfMovies ( userId ); ¡ ¡ ¡ ¡def ¡Observable < VideoBookmark > ¡getBookmark ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoRating > ¡getRating ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoMetadata > ¡getMetadata ( videoId ); ¡ } Local Cache Collapsed Network Call

  32. Implementation Can Differ and Change BIO NIO Network Call class ¡ VideoService ¡ { ¡ ¡ ¡ ¡ def ¡Observable < VideoList > ¡getPersonalizedListOfMovies ( userId ); ¡ ¡ ¡ ¡def ¡Observable < VideoBookmark > ¡getBookmark ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoRating > ¡getRating ( userId, ¡videoId ); ¡ ¡ ¡ ¡def ¡Observable < VideoMetadata > ¡getMetadata ( videoId ); ¡ } Local Cache Collapsed Collapsed Network Call Network Call

  33. Retrieval, Transformation, Combination all done in same declarative manner

  34. What about … ?

  35. Error Handling

  36. Observable.create(subscriber -> { throw new RuntimeException("failed!"); }).onErrorResumeNext(throwable -> { return Observable.just("fallback value"); }).subscribe(System.out::println);

  37. Observable.create(subscriber -> { throw new RuntimeException("failed!"); }).onErrorReturn(throwable -> { return "fallback value"; }).subscribe(System.out::println);

  38. Observable.create(subscriber -> { throw new RuntimeException("failed!"); }).retryWhen(attempts -> { return attempts.zipWith(Observable.range(1, 3), (throwable, i) -> i) .flatMap(i -> { System.out.println("delay retry by " + i + " second(s)"); return Observable.timer(i, TimeUnit.SECONDS); }).concatWith(Observable.error(new RuntimeException("Exceeded 3 retries"))); }) .subscribe(System.out::println, t -> t.printStackTrace());

  39. Observable.create(subscriber -> { throw new RuntimeException("failed!"); }).retryWhen(attempts -> { return attempts.zipWith(Observable.range(1, 3), (throwable, i) -> i) .flatMap(i -> { System.out.println("delay retry by " + i + " second(s)"); return Observable.timer(i, TimeUnit.SECONDS); }).concatWith(Observable.error(new RuntimeException("Exceeded 3 retries"))); }) .subscribe(System.out::println, t -> t.printStackTrace());

  40. Observable.create(subscriber -> { throw new RuntimeException("failed!"); }).retryWhen(attempts -> { return attempts.zipWith(Observable.range(1, 3), (throwable, i) -> i) .flatMap(i -> { System.out.println("delay retry by " + i + " second(s)"); return Observable.timer(i, TimeUnit.SECONDS); }).concatWith(Observable.error(new RuntimeException("Exceeded 3 retries"))); }) .subscribe(System.out::println, t -> t.printStackTrace());

  41. Observable.create(subscriber -> { throw new RuntimeException("failed!"); }).retryWhen(attempts -> { return attempts.zipWith(Observable.range(1, 3), (throwable, i) -> i) .flatMap(i -> { System.out.println("delay retry by " + i + " second(s)"); return Observable.timer(i, TimeUnit.SECONDS); }).concatWith(Observable.error(new RuntimeException("Exceeded 3 retries"))); }) .subscribe(System.out::println, t -> t.printStackTrace());

  42. Concurrency

  43. Concurrency an Observable is sequential (no concurrent emissions) scheduling and combining Observables enables concurrency while retaining sequential emission

  44. // merging async Observables allows each // to execute concurrently Observable.merge(getDataAsync(1), getDataAsync(2)) merge

  45. // concurrently fetch data for 5 items Observable.range(0, 5).flatMap(i -> { return getDataAsync(i); })

  46. Observable.range(0, 5000).window(500).flatMap(work -> { return work.observeOn(Schedulers.computation()) .map(item -> { // simulate computational work try { Thread.sleep(1); } catch (Exception e) {} return item + " processed " + Thread.currentThread(); }); })

  47. Observable.range(0, 5000).buffer(500).flatMap(is -> { return Observable.from(is).subscribeOn(Schedulers.computation()) .map(item -> { // simulate computational work try { Thread.sleep(1); } catch (Exception e) {} return item + " processed " + Thread.currentThread(); }); })

  48. Flow Control

  49. Flow Control (backpressure)

  50. Observable.from(iterable).take(1000).map(i -> "value_" + i).subscribe(System.out::println); no backpressure needed

  51. Observable.from(iterable).take(1000).map(i -> "value_" + i).subscribe(System.out::println); no backpressure needed synchronous on same thread (no queueing)

  52. Observable.from(iterable).take(1000).map(i -> "value_" + i) .observeOn(Schedulers.computation()).subscribe(System.out::println); backpressure needed

  53. Observable.from(iterable).take(1000).map(i -> "value_" + i) .observeOn(Schedulers.computation()).subscribe(System.out::println); backpressure needed asynchronous (queueing)

  54. Flow Control Options

  55. Hot Cold emits when requested emits whether you’re ready or not (generally at controlled rate) examples examples mouse and keyboard events database query system events web service request stock prices reading file Observable.create(subscriber -> { Observable.create(subscriber -> { // register with data source // fetch data }) }) flow control flow control & backpressure

  56. Block (callstack blocking and/or park the thread) Hot or Cold Streams

  57. Temporal Operators (batch or drop data using time) Hot Streams

  58. Observable.range(1, 1000000).sample(10, TimeUnit.MILLISECONDS).forEach(System.out::println); 110584 242165 544453 942880

  59. Observable.range(1, 1000000).throttleFirst(10, TimeUnit.MILLISECONDS).forEach(System.out::println); 1 55463 163962 308545 457445 592638 751789 897159

  60. Observable.range(1, 1000000).debounce(10, TimeUnit.MILLISECONDS).forEach(System.out::println); 1000000

  61. Observable.range(1, 1000000).buffer(10, TimeUnit.MILLISECONDS) .toBlocking().forEach(list -> System.out.println("batch: " + list.size())); batch: 71141 batch: 49488 batch: 141147 batch: 141432 batch: 195920 batch: 240462 batch: 160410

  62. /* The following will emit a buffered list as it is debounced */ // first we multicast the stream ... using refCount so it handles the subscribe/unsubscribe Observable<Integer> burstStream = intermittentBursts().take(20).publish().refCount(); // then we get the debounced version Observable<Integer> debounced = burstStream.debounce(10, TimeUnit.MILLISECONDS); // then the buffered one that uses the debounced stream to demark window start/stop Observable<List<Integer>> buffered = burstStream.buffer(debounced); // then we subscribe to the buffered stream so it does what we want buffered.toBlocking().forEach(System.out::println); [0, 1, 2] [0, 1, 2] [0, 1, 2, 3, 4, 5, 6] [0, 1, 2, 3, 4] [0, 1] []

  63. /* The following will emit a buffered list as it is debounced */ // first we multicast the stream ... using refCount so it handles the subscribe/unsubscribe Observable<Integer> burstStream = intermittentBursts().take(20).publish().refCount(); // then we get the debounced version Observable<Integer> debounced = burstStream.debounce(10, TimeUnit.MILLISECONDS); // then the buffered one that uses the debounced stream to demark window start/stop Observable<List<Integer>> buffered = burstStream.buffer(debounced); // then we subscribe to the buffered stream so it does what we want buffered.toBlocking().forEach(System.out::println); [0, 1, 2] [0, 1, 2] [0, 1, 2, 3, 4, 5, 6] [0, 1, 2, 3, 4] [0, 1] []

  64. /* The following will emit a buffered list as it is debounced */ // first we multicast the stream ... using refCount so it handles the subscribe/unsubscribe Observable<Integer> burstStream = intermittentBursts().take(20).publish().refCount(); // then we get the debounced version Observable<Integer> debounced = burstStream.debounce(10, TimeUnit.MILLISECONDS); // then the buffered one that uses the debounced stream to demark window start/stop Observable<List<Integer>> buffered = burstStream.buffer(debounced); // then we subscribe to the buffered stream so it does what we want buffered.toBlocking().forEach(System.out::println); [0, 1, 2] [0, 1, 2] [0, 1, 2, 3, 4, 5, 6] [0, 1, 2, 3, 4] [0, 1] []

Recommend


More recommend