NetflixOSS season 2 episode 2 - Reactive / Async

Download NetflixOSS   season 2 episode 2 - Reactive / Async

Post on 19-Aug-2014

1.716 views

Category:

Engineering

1 download

DESCRIPTION

NetflixOSS Season 2 Episode 2 Meetup, Reactive/Async theme. Lightning talks by Netflix engineers, as well as guest speakers from Square, Couchbase and Typesafe.

TRANSCRIPT

Season 2 Episode 2 July 9, 2014 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 Netflix Lightning talks Composable Functions Reactively Applied Composable Functions Reactively Applied Composable Functions Reactively Applied 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); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); return new UserCommand(userId).observe().flatMap(user -> { Observable catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -> { return catalogList.videos(). flatMap(video -> { Observable bookmark = new BookmarkCommand(video).observe(); Observable rating = new RatingsCommand(video).observe(); Observable metadata = new VideoMetadataCommand(video).observe(); return Observable.zip(bookmark, rating, metadata, (b, r, m) -> { return combineVideoData(video, b, r, m); }); }); }); Observable 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); }); + 1.3.x (current production) 1.4 Release Candidate (available now) - support for fully non-blocking 1.4.0 Final (coming months) 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) github.com/Netflix/RxJava github.com/ReactiveX/RxJava Version 1.x to be released on Maven Central with groupId io.reactivex.* 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.* Along the way we started thinking about non-blocking IO ... WSPerfLab https://github.com/Netflix-Skunkworks/WSPerfLab WSPerfLab https://github.com/Netflix-Skunkworks/WSPerfLab Best case latency for response is ~155ms RxNetty Performance client: wrk -t 4 -c 400 server CPUs ~2% idle NOTE: It is still early days for RxNetty tuning; more to come Tuning for an acceptable latency distribution client: ab server CPU usage varies with load 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 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. Approaching light speed... Hello World All platforms are 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... Visualizing Tomcat Overheads Flame graph of Java CPU time for Hello World benchmark Thread and I/O management overheads 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 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, ... RxNetty & Karyon 2.0 @NiteshKant 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 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 We want an extremely performant IPC library Heard this before? Reinventing The Wheel? Image Courtesy: Oxford Creativity Finagle Vert.x Ratpack Tons of Netty based Frameworks? On the lookout .. Low level control. Traffic Shaping. (Zuul) Backpressure. Netflix fit Reactive programming model. Netflix Components (Eureka, Archaius, Governator, etc.) RxNetty Reactive Extensions To Netty Nucleus of Netflixs IPC stack Powering Ribbon 2.0 & Karyon 2.0 https://github.com/Netflix/RxNetty Example (Server Sent Events) RxNetty.createHttpServer(8887, (request, response) -> { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -> { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.sseServerConfigurator()).start(); Server Client RxNetty.createHttpClient("127.0.0.1", 8887, PipelineConfigurators.sseClientConfigurator()) .submit(HttpClientRequest.createGet("/")) .flatMap(response -> response.getContent()) .toBlocking().forEach(event -> System.out.println(event)) data: interval: 0 data: interval: 1 data: interval: 2 data: interval: 3 Output ... data: interval: 4 data: interval: 5 Example (Stream Aggregation) RxNetty.createHttpServer(8887, (request, response) -> { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -> { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.sseServerConfigurator()).start(); { } *3 Example (Stream Aggregation) Observable.merge(RxNetty.createHttpClient("127.0.0.1", 8887...), RxNetty.createHttpClient("127.0.0.1", 8888...), RxNetty.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(); 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 Karyon 2.0 Adds Netflix constructs (governator, eureka, archaius, etc.) on RxNetty. Provides extensions (servlet, jersey, etc.) to RxNetty. More palatable to application developers. 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 interceptorSupport) { interceptorSupport.forUri("/hello").interceptIn(AuthInterceptor.class); } } } Ribbon 2 Allen Wang Tendency to become fat with boilerplate code Hystrix integration Go async Declare a client, rather than write a client Ribbon 2.0 - Motivation Ribbon 2.0: Lets tie em up 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 Ribbon 2.0 - Annotation Example public interface MovieService { RibbonRequest 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 result = movieService.recommendationsByUserId(user1) .toObservable(); Ribbon 2: Template Example HttpResourceGroup movieService = Ribbon.createHttpResourceGroup("movieService"); HttpRequestTemplate template1 = movieService.newRequestTemplate("recommendationsByUserId", ByteBuf.class) .withMethod("GET") .withUriTemplate("/users/{userId}/recommendations") .withFallbackProvider(new RecommendationServiceFallbackHandler()); RibbonRequest request = template1.requestBuilder() .withRequestProperty("userId", user1).build(); Observable result = request.toObservable(); // non blocking ByteBuf result = request.execute(); // blocking Ribbon 2.0 - Other New Features Ribbons 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 Reactive outside Netflix Retrofit Jake Wharton Retrofit Example: GitHub API List Contributors GET /repos/:owner/:repo/contributors Retrofit GET /repos/:owner/:repo/contributors interface GitHub { List contributors( String owner, String repo); } class Contributor { String login; long contributions; } Retrofit GET /repos/:owner/:repo/contributors interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List contributors( String owner, String repo); } Retrofit GET /repos/:owner/:repo/contributors interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List contributors( @Path("owner") String owner, @Path("repo") String repo); } Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com/") .build(); GitHub gitHub = restAdapter.create(GitHub.class); List contributors = gitHub.contributors("netflix", "rxjava"); Retrofit List 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 ... Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .build(); Retrofit interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List contributors( @Path("owner") String owner, @Path("repo") String repo); } Retrofit interface GitHub { @GET("/repos/{owner}/{repo}/contributors?anon=true") List contributors( @Path("owner") String owner, @Path("repo") String repo); } Retrofit interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List contributors( @Path("owner") String owner, @Path("repo") String repo, @Query("anon") boolean includeAnonymous); } 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 config; List events; boolean active; } Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .setClient(new OkClient()) .build(); 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(); Retrofit Form URL encoding Response streaming Request interceptors Dynamic headers Android Multipart Logging Mock RestAdapter Asynchronous invoke AppEngine ...and more! Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List contributors( @Path("owner") String owner, @Path("repo") String repo); } Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable contributors( @Path("owner") String owner, @Path("repo") String repo); } 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 ... Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable contributors( @Path("owner") String owner, @Path("repo") String repo); @GET("/users/{user}") Observable user( @Path("user") String user); } 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 ... 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 Kppler Justin Ryan Mairbek Khadikov ... Retrofit + RxJava interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable contributors( @Path("owner") String owner, @Path("repo") String repo); @GET("/users/{user}/starred") Observable starred( @Path("user") String user); } 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); 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 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); 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 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 Retrofit square.github.io/retrofit Couchbase Matt Ingenthron Couchbase is... Document-oriented (JSON) database ... Low latency & high throughput Highly available and scalable ... Motivations with Couchbase Java Simplify ... Even *more* performance Build for the JVM, not for the Java language Have Scala, JRuby already being worked on 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 while (true) { client.set("foo", "bar") }; while (true) { client.set("foo", "bar") }; Signature: public OperationFuture set(java.lang.String key, java.lang.Object value); while (true) { client.set("foo", "bar").get() }; What were Building On A set of new components RxJava (both internal and public interface) Netty (internal only) LMAX Disruptor RxJava for Couchbase Will be part of the clients 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 faade helps build toward Scala and others Getting Referenced Elements client.asyncGet("posts::1").addListener(future -> { if (future.isDone() && !future.isCancelled()) { String content = (String) future.get(); List 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()); } }); } } }); 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())); Beauty of Error Handling return super.registerBucket(name, password).flatMap(new Func1() { @Override public Observable call(Boolean aBoolean) { return cluster().send(new BucketStreamingRequest(TERSE_PATH, name, password)); } }).onErrorResumeNext(new Func1() { @Override public Observable call(Throwable throwable) { return cluster().send(new BucketStreamingRequest(VERBOSE_PATH, name, password)); } }) Couchbase React to our Dev Preview 2! Michael Nitschingers blog: http://blog.couchbase.com/couchbase-java-sdk-200-developer-preview-2 Five Minutes of Futures Will Sargent, Typesafe Reactive Interface In Theory Looks like: Responsive? Scalable? Resilient? Event-Driven? Reactive Interface in Practice Really looks like this: trait FooService { def find(fooId:FooID) : Future[Option[Foo]] } Plain English Non-blocking / Async means either Future[T] or Actor model Choices To Akka, or Not to Akka? Akka with Futures Akka to Future: val myFuture = actorRef ? message Future to Akka: futureStuff pipeTo actorRef Assuming a Services based API val fooFuture = fooService.get(1) val barFuture = barService.get(2) val quuxFuture = quuxService.get(3) ...Simple, right? Not so fast val fooFuture = fooService.get(1) val barFuture = barService.get(foo.barId) val quuxFuture = quuxService.get(bar.quuxId) ...wat. FLATMAP THAT fooService.get(1).map { maybeFoo => maybeFoo.flatMap { foo => val barFuture = barService.get(foo.barId) // do the same for bar & quux } } For comprehensions! for { maybeFoo Future[Result])): Future[Result] = { futureOptionBlock.flatMap { case Some(found) => foundBlock(found) case None => Future.successful(NotFound) } } Whats next? Demos, food and drinks! August 20th - NetflixOSS and Cloud Security