Advertisement
Advertisement

More Related Content

Advertisement
Advertisement

NetflixOSS season 2 episode 2 - Reactive / Async

  1. Season 2 Episode 2 July 9, 2014
  2. Evening Outline Lightning Talks: ● Reactive / Rx ● RxJava ● RxNetty ● Ribbon 2.0 ● Karyon 2.0 Guest Speakers ● Jake Wharton, Square ● Matt Ingenthron, Couchbase ● Will Sargent, Typesafe
  3. Netflix Lightning talks
  4. Composable Functions Reactively Applied
  5. Composable Functions Reactively Applied
  6. Composable Functions Reactively Applied
  7. Clojure Scala Groovy JRuby Java 8 (-> (Observable/from ["one" "two" "three"]) (.take 2) (.subscribe (rx/action [arg] (println arg)))) Observable("one", "two", "three") .take(2) .subscribe((arg: String) => { println(arg) }) Observable.from("one", "two", "three") .take(2) .subscribe(lambda { |arg| puts arg }) Observable.from("one", "two", "three") .take(2) .subscribe({arg -> println(arg)}) Observable.from("one", "two", "three") .take(2) .subscribe(System.out::println);
  8. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  9. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  10. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  11. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  12. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  13. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  14. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  15. return new UserCommand(userId).observe().flatMap(user -> { Observable<Map<String, Object>> catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos().<Map<String, Object>> flatMap(video -> { Observable<Bookmark> bookmark = new BookmarkCommand(video).observe(); Observable<Rating> rating = new RatingsCommand(video).observe(); Observable<VideoMetadata> metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable<Map<String, Object>> social = new SocialCommand(user).observe().map(s -> { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -> { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); });
  16. +
  17. 1.3.x (current production) 1.4 Release Candidate (available now) - support for fully non-blocking 1.4.0 Final (coming months)
  18. 0.19.x (current production) 0.20 Release Candidates (coming weeks) - the “backpressure” release 0.20.0 Final 1.0 Release Candidates (Summer 2014) - no more breaking changes 1.0.0 Final (Summer/Fall 2014)
  19. github.com/Netflix/RxJava github.com/ReactiveX/RxJava Version 1.x to be released on Maven Central with groupId io.reactivex.*
  20. github.com/Netflix/RxJava github.com/ReactiveX/RxJava github.com/ReactiveX/RxScala github.com/ReactiveX/RxGroovy github.com/ReactiveX/RxClojure github.com/ReactiveX/RxKotlin github.com/ReactiveX/RxJRuby github.com/ReactiveX/RxAndroid etc ... Version 1.x to be released on Maven Central with groupId io.reactivex.*
  21. Along the way we started thinking about non-blocking IO ...
  22. WSPerfLab https://github.com/Netflix-Skunkworks/WSPerfLab
  23. WSPerfLab https://github.com/Netflix-Skunkworks/WSPerfLab Best case latency for response is ~155ms
  24. RxNetty Performance client: wrk -t 4 -c 400 server CPUs ~2% idle NOTE: It is still early days for RxNetty tuning; more to come
  25. Tuning for an acceptable latency distribution client: ab server CPU usage varies with load
  26. RxNetty vs Tomcat Event loop model more efficient than thread pools: ● Reduces thread CPU overheads ● Reduced thread lock contention ● Better work scheduling: platform chooses, instead of the kernel scheduling threads ● Warmer CPU caches and memory locality on NUMA: event worker threads can have better CPU affinity
  27. RxNetty Micro Benchmarking Server: AWS c3.xlarge, tuned to reduce perturbations Client: Multi-threaded (wrk -t 4 -c 100/400), 1 min warmup ● Sufficient load to exhaust CPU capacity. Active Benchmarking Methodology: ● Tools: sysstat, perf_events, SystemTap, flame graphs, … ● USE Method, static performance tuning, etc. ● Target analyzed during benchmark confirm target results and identify (and tune) true limiters.
  28. Approaching light speed...
  29. Hello World ● All platforms are <100us for Hello World, so their CPU overheads are already negligible for this workload. ○ Consider application compute times of >1ms ● Hence testing “Hello Netflix” as well: a basic Netflix service Hello World with dependency requests. ● Although, Hello World max latency (not pictured) was still much higher for Tomcat...
  30. Visualizing Tomcat Overheads ● Flame graph of Java CPU time for Hello World benchmark ● Thread and I/O management overheads
  31. Visualizing RxNetty Overheads ● Flame graph of Java CPU time for Hello World ● Time in read() and write() (doing I/O; ie, doing “work”) dominates ● Flame graphs also studied of kernel and user-time ● Many improvements found and fixed
  32. Blog post forthcoming… Keywords: ● Oracle JDK 1.8.0 (others tried); Xmx & Xms; different GCs and settings, eg -XX:+UseConcMarkSweepGC ● Apache-tomcat 7.0.54: tested BIO, NIO, APR; maxThreads=150 or 512; maxQueueSize=up to 400; acceptCount=up to 100; disabled access log, ... ● Node.js 0.6.12 with clustering (faster than 0.10/0.11) ● vert.x 2.1 with -instances 4 ● Java profilers: Google lightweight java profiler; JFR; … ● perf_events; SystemTap; flame graphs, ...
  33. RxNetty & Karyon 2.0 @NiteshKant
  34. Netflix IPC Stack (1.0) A p a c h e H T T P C l i e n t Eureka (Service Registry) Server (Karyon) Apache Tomcat Client H y s t r i x E V C a c h e Ribbon Load Balancing Eureka Integration Metrics (Servo) Bootstrapping (Governator) Metrics (Servo) Admin ConsoleHTTP Eureka Integration Registration Fetch Registry A Blocking Architecture
  35. Netflix IPC Stack (2.0) Client (Ribbon 2.0) Eureka (Service Registry) Server (Karyon) Ribbon Transport Load Balancing Eureka Integration Metrics (Servo) Bootstrapping (Governator) Metrics (Servo) Admin Console HTTP Eureka Integration Registration Fetch Registry Ribbon Hystrix EVCache R x N e t t y RxNetty UDP TCP WebSockets SSE A Completely Reactive Architecture
  36. “We want an extremely performant IPC library” Heard this before?
  37. Reinventing The Wheel? Image Courtesy: Oxford Creativity Finagle Vert.x Ratpack Tons of Netty based Frameworks?
  38. On the lookout ….. ● Low level control. ○ Traffic Shaping. (Zuul) ○ Backpressure. ● Netflix “fit” ○ Reactive programming model. ○ Netflix Components (Eureka, Archaius, Governator, etc.)
  39. RxNetty Reactive Extensions To Netty Nucleus of Netflix’s IPC stack Powering Ribbon 2.0 & Karyon 2.0 https://github.com/Netflix/RxNetty
  40. Example (Server Sent Events) RxNetty.createHttpServer(8887, (request, response) -> { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -> { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.<ByteBuf>sseServerConfigurator()).start(); Server Client RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8887, PipelineConfigurators.sseClientConfigurator()) .submit(HttpClientRequest.<ByteBuf>createGet("/")) .flatMap(response -> response.getContent()) .toBlocking().forEach(event -> System.out.println(event))
  41. data: interval: 0 data: interval: 1 data: interval: 2 data: interval: 3 Output …... data: interval: 4 data: interval: 5
  42. Example (Stream Aggregation) RxNetty.createHttpServer(8887, (request, response) -> { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -> { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.<ByteBuf>sseServerConfigurator()).start(); { } *3
  43. Example (Stream Aggregation) Observable.merge(RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8887...), RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8888...), RxNetty.<ByteBuf, ServerSentEvent>createHttpClient("127.0.0.1", 8889...)) ).toBlocking().forEach(event -> System.out.println(event)) RxNetty.createHttpServer(8887, ...).start(); RxNetty.createHttpServer(8888, ...).start(); RxNetty.createHttpServer(8889, ...).start();
  44. data: interval (8887): 0 data: interval (8888): 0 data: interval (8887): 1 data: interval (8887): 2 Output …... data: interval (8889): 0 data: interval (8888): 1
  45. Karyon 2.0 ● Adds Netflix constructs (governator, eureka, archaius, etc.) on RxNetty. ● Provides extensions (servlet, jersey, etc.) to RxNetty. ● More palatable to application developers.
  46. Example @ArchaiusBootstrap @KaryonBootstrap(name = "hello-netflix-oss") @Modules(include = {HelloNossApp.KaryonJerseyModuleImpl.class, KaryonWebAdminModule.class, KaryonEurekaModule.class}) public final class HelloNossApp { public static class KaryonJerseyModuleImpl extends KaryonJerseyModule { protected void configure() { super.configure(); bind(AuthenticationService.class).to(AuthenticationServiceImpl.class); } public int serverPort() { return 8888; } public int shutdownPort() { return 8899; } public void configureInterceptors(GovernatorHttpInterceptorSupport<ByteBuf, ByteBuf> interceptorSupport) { interceptorSupport.forUri("/hello").interceptIn(AuthInterceptor.class); } } }
  47. Ribbon 2 Allen Wang
  48. ● Tendency to become “fat” with boilerplate code ● Hystrix integration ● Go “async” ● “Declare” a client, rather than “write” a client Ribbon 2.0 - Motivation
  49. Ribbon 2.0: Let’s tie ‘em up
  50. Ribbon 2.0 - New “ribbon” module ● Template support for building HTTP request ● Rx style non-blocking and blocking APIs ● Hystrix built-in for fault tolerance ● Pluggable cache access with EVCache implementation ● Annotation based client creation as an alternative
  51. Ribbon 2.0 - Annotation Example public interface MovieService { RibbonRequest<Recommendations> recommendationsByUserId(@Var("userId") String userId); } @TemplateName(“recommendations”) @Http(method = HttpMethod.GET, uriTemplate="/users/{userId}/recommendations") @Hystrix(fallbackHandler = RecommendationFallbackHandler.class) @EvCache(name = "movie-rec", appName = "movieService", cacheKeyTemplate = "{userId}") MovieService movieService = Ribbon.from(MovieService.class); Observable<Recommendations> result = movieService.recommendationsByUserId(“user1”) .toObservable();
  52. Ribbon 2: Template Example HttpResourceGroup movieService = Ribbon.createHttpResourceGroup("movieService"); HttpRequestTemplate<ByteBuf> template1 = movieService.newRequestTemplate("recommendationsByUserId", ByteBuf.class) .withMethod("GET") .withUriTemplate("/users/{userId}/recommendations") .withFallbackProvider(new RecommendationServiceFallbackHandler()); RibbonRequest<ByteBuf> request = template1.requestBuilder() .withRequestProperty("userId", “user1”).build(); Observable<ByteBuf> result = request.toObservable(); // non blocking ByteBuf result = request.execute(); // blocking
  53. Ribbon 2.0 - Other New Features ● Ribbon’s async module - “ribbon-transport” ○ HTTP, TCP and UDP clients on top of RxNetty with load balancing capability ● New load balancing features ○ Command pattern load balancing APIs implemented with Rx for easy integration for third party client ○ Shuffle sharding server list for fault isolation
  54. Reactive outside Netflix
  55. Retrofit Jake Wharton
  56. Retrofit Example: GitHub API List Contributors GET /repos/:owner/:repo/contributors
  57. Retrofit GET /repos/:owner/:repo/contributors interface GitHub { List<Contributor> contributors( String owner, String repo); } class Contributor { String login; long contributions; }
  58. Retrofit GET /repos/:owner/:repo/contributors interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( String owner, String repo); }
  59. Retrofit GET /repos/:owner/:repo/contributors interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo); }
  60. Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com/") .build(); GitHub gitHub = restAdapter.create(GitHub.class); List<Contributor> contributors = gitHub.contributors("netflix", "rxjava");
  61. Retrofit List<Contributor> contributors = gitHub.contributors("netflix", "rxjava"); for (Contributor c : contributors) { println(c.login + 't' + c.contributions); } 1483 benjchristensen 225 zsxwing 167 samuelgruetter 146 jmhofer 137 akarnokd 105 DavidMGross 102 AppliedDuality ...
  62. Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .build();
  63. Retrofit interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo); }
  64. Retrofit interface GitHub { @GET("/repos/{owner}/{repo}/contributors?anon=true") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo); }
  65. Retrofit interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo, @Query("anon") boolean includeAnonymous); }
  66. Retrofit interface GitHub { @POST("/repos/{owner}/{repo}/hooks") Response createHook( @Path("owner") String owner, @Path("repo") String repo, @Body Hook hook); } class Hook { String name; Map<String, Object> config; List<String> events; boolean active; }
  67. Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .setClient(new OkClient()) .build();
  68. Retrofit OkHttpClient client = new OkHttpClient(); client.setProtocols(Arrays.asList(HTTP_2)); RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .setClient(new OkClient(client)) .build();
  69. Retrofit ● Form URL encoding ● Response streaming ● Request interceptors ● Dynamic headers ● Android ● Multipart ● Logging ● Mock RestAdapter ● Asynchronous invoke ● AppEngine ...and more!
  70. Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo); }
  71. Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); }
  72. Retrofit + RxJava gitHub.contributors("netflix", "rxjava") .lift(flattenList()) .forEach(c -> println(c.login + 't' + c.contributions)); 1483 benjchristensen 225 zsxwing 167 samuelgruetter 146 jmhofer 137 akarnokd 105 DavidMGross 102 AppliedDuality ...
  73. Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); @GET("/users/{user}") Observable<User> user( @Path("user") String user); }
  74. Retrofit + RxJava gitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.user(c.login)) .forEach(user -> println(user.name)); Ben Christensen Shixiong Zhu null Joachim Hofer null David Gross null ...
  75. Retrofit + RxJava gitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.user(c.login)) .filter(user -> user.name != null) .forEach(user -> println(user.name)); Ben Christensen Shixiong Zhu Joachim Hofer David Gross Matthias Käppler Justin Ryan Mairbek Khadikov ...
  76. Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); @GET("/users/{user}/starred") Observable<List<Repo>> starred( @Path("user") String user); }
  77. Retrofit + RxJava gitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.starred(c.login)) .lift(flattenList()) .groupBy(r -> r.full_name) .flatMap(g -> g.count().map(c -> c + "t" + g.getKey())) .toSortedList((a, b) -> b.compareTo(a)) .lift(flattenList()) .take(8) .forEach(Main::println);
  78. Retrofit + RxJava 7 Netflix/RxJava 2 twitter/finagle 2 scala/scala 2 mbostock/d3 2 kpelykh/docker-java 2 Netflix/zuul 2 Netflix/feign 2 Netflix/archaius
  79. Retrofit + RxJava gitHub.contributors("square", "retrofit") .lift(flattenList()) .flatMap(c -> gitHub.starred(c.login)) .lift(flattenList()) .groupBy(r -> r.full_name) .flatMap(g -> g.count().map(c -> c + "t" + g.getKey())) .toSortedList((a, b) -> b.compareTo(a)) .lift(flattenList()) .take(8) .forEach(Main::println);
  80. gitHub.contributors("square", "retrofit") .lift(flattenList()) .flatMap(c -> gitHub.starred(c.login)) .lift(flattenList()) .filter(r -> !r.full_name.startsWith("square/")) .groupBy(r -> r.full_name) .flatMap(g -> g.count().map(c -> c + "t" + g.getKey())) .toSortedList((a, b) -> b.compareTo(a)) .lift(flattenList()) .take(8) .forEach(Main::println); Retrofit + RxJava
  81. Retrofit + RxJava 4 frankiesardo/auto-parcel 4 Comcast/FreeFlow 3 xxv/android-lifecycle 3 robolectric/robolectric 3 inmite/android-butterknife-zelezny 3 google/auto 3 facebook/rebound 3 etsy/AndroidStaggeredGrid 3 Netflix/RxJava
  82. Retrofit square.github.io/retrofit
  83. Couchbase Matt Ingenthron
  84. Couchbase is... Document-oriented (JSON) database ... Low latency & high throughput … Highly available and scalable ...
  85. Motivations with Couchbase Java ➢ Simplify ... ➢ Even *more* performance ➢ Build for the JVM, not for the Java language ○ Have Scala, JRuby already being worked on
  86. History of Concurrency Core Java component, spymemcached, has used Futures since 2006 or so. ➢ Always been ahead of most other database like clients, however… ➢ Difficult for developers to code against ○ Dumb benchmarks turn into FAQs
  87. while (true) { client.set("foo", "bar") };
  88. while (true) { client.set("foo", "bar") }; Signature: public OperationFuture<java.lang.Boolean> set(java.lang.String key, java.lang.Object value);
  89. while (true) { client.set("foo", "bar").get() };
  90. What we’re Building On ➢ A set of new components ○ RxJava (both internal and public interface) ○ Netty (internal only) ○ LMAX Disruptor
  91. RxJava for Couchbase ➢ Will be part of the client’s public API ○ Helps us support Java 8, but is also backward compatible ■ Generally get a great Java 8 experience for free! ➢ Also using RxJava extensively internally ○ Breaking the project into core and language façade helps build toward Scala and others
  92. Getting Referenced Elements client.asyncGet("posts::1").addListener(future -> { if (future.isDone() && !future.isCancelled()) { String content = (String) future.get(); List<String> ids = comments_ids_from_json(content); int i = 0; for (String id : ids) { if (i++ == 5) { break; } client.asyncGet(id).addListener(future1 -> { if (future1.isDone() && !future1.isCancelled()) { System.out.println(future1.get()); } }); } } });
  93. Getting Referenced Elements Rx Way bucket .get("posts::1") .map(document -> document.content().getArray("comments")) .flatMap(comments -> Observable.from(comments.toList())) .take(5) .filter(o -> o instanceof String) .flatMap(o -> bucket.get((String) o)) .subscribe(doc -> System.out.println(doc.content()));
  94. Beauty of Error Handling return super.registerBucket(name, password).flatMap(new Func1<Boolean, Observable<BucketStreamingResponse>>() { @Override public Observable<BucketStreamingResponse> call(Boolean aBoolean) { return cluster().send(new BucketStreamingRequest(TERSE_PATH, name, password)); } }).onErrorResumeNext(new Func1<Throwable, Observable<BucketStreamingResponse>>() { @Override public Observable<BucketStreamingResponse> call(Throwable throwable) { return cluster().send(new BucketStreamingRequest(VERBOSE_PATH, name, password)); } })
  95. Couchbase React to our Dev Preview 2! Michael Nitschinger’s blog: http://blog.couchbase.com/couchbase-java-sdk-200-developer-preview-2
  96. Five Minutes of Futures Will Sargent, Typesafe
  97. Reactive Interface In Theory Looks like: ● Responsive? ● Scalable? ● Resilient? ● Event-Driven?
  98. Reactive Interface in Practice Really looks like this: trait FooService { def find(fooId:FooID) : Future[Option[Foo]] }
  99. Plain English “Non-blocking” / “Async” means either Future[T] or Actor model
  100. Choices To Akka, or Not to Akka?
  101. Akka with Futures Akka to Future: val myFuture = actorRef ? message Future to Akka: futureStuff pipeTo actorRef
  102. Assuming a Services based API val fooFuture = fooService.get(1) val barFuture = barService.get(2) val quuxFuture = quuxService.get(3) ...Simple, right?
  103. Not so fast val fooFuture = fooService.get(1) val barFuture = barService.get(foo.barId) val quuxFuture = quuxService.get(bar.quuxId) ...wat.
  104. FLATMAP THAT fooService.get(1).map { maybeFoo => maybeFoo.flatMap { foo => val barFuture = barService.get(foo.barId) // do the same for bar & quux } }
  105. For comprehensions! for { maybeFoo <- fooService.get(1) } yield { for { // Option / Future does not compose foo <- maybeFoo } yield ... }
  106. Scala Async! async { val maybeFoo = await(fooService.get(1)) maybeFoo.flatMap { foo => val bar = await(barService.get(2)) } }
  107. Scala Async Sadness COMPILE ERROR. “await must not be used inside a closure nested within an async block” == no flatMap of that.
  108. Monads don’t compose But before reaching for readers/transformers: ● Use small methods ● Short circuit ● Use loan patterns
  109. Use Small Methods def foo2bar(ff:Future[Foo]) : Future[Bar]
  110. Short Circuit implicit def flatten[A](fofoa: Future[Option [Future[Option[A]]]]): Future[Option[A]] = fofoa. flatMap(_.getOrElse(Future.successful(None)))
  111. Loan Pattern def getFuture[T](futureOptionBlock: Future[Option[T]])(foundBlock: (T => Future[Result])): Future[Result] = { futureOptionBlock.flatMap { case Some(found) => foundBlock(found) case None => Future.successful(NotFound) } }
  112. What’s next? Demos, food and drinks! August 20th - NetflixOSS and Cloud Security
Advertisement