NetflixOSS season 2 episode 2 - Reactive / Async

Download NetflixOSS   season 2 episode 2 - Reactive / Async

Post on 19-Aug-2014

1.725 views

Category:

Engineering

1 download

Embed Size (px)

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

<ul><li> Season 2 Episode 2 July 9, 2014 </li> <li> 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 </li> <li> Netflix Lightning talks </li> <li> Composable Functions Reactively Applied </li> <li> Composable Functions Reactively Applied </li> <li> Composable Functions Reactively Applied </li> <li> Clojure Scala Groovy JRuby Java 8 (-&gt; (Observable/from ["one" "two" "three"]) (.take 2) (.subscribe (rx/action [arg] (println arg)))) Observable("one", "two", "three") .take(2) .subscribe((arg: String) =&gt; { println(arg) }) Observable.from("one", "two", "three") .take(2) .subscribe(lambda { |arg| puts arg }) Observable.from("one", "two", "three") .take(2) .subscribe({arg -&gt; println(arg)}) Observable.from("one", "two", "three") .take(2) .subscribe(System.out::println); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> return new UserCommand(userId).observe().flatMap(user -&gt; { Observable&gt; catalog = new PersonalizedCatalogCommand(user).observe() .flatMap(catalogList -&gt; { return catalogList.videos().&gt; flatMap(video -&gt; { 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) -&gt; { return combineVideoData(video, b, r, m); }); }); }); Observable&gt; social = new SocialCommand(user).observe().map(s -&gt; { return s.getDataAsMap(); }); return Observable.merge(catalog, social); }).flatMap(data -&gt; { return response.writeAndFlush(new ServerSentEvent("", "data", SimpleJson.mapToJson(data)), EdgeServer.SSE_TRANSFORMER); }); </li> <li> + </li> <li> 1.3.x (current production) 1.4 Release Candidate (available now) - support for fully non-blocking 1.4.0 Final (coming months) </li> <li> 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) </li> <li> github.com/Netflix/RxJava github.com/ReactiveX/RxJava Version 1.x to be released on Maven Central with groupId io.reactivex.* </li> <li> 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.* </li> <li> Along the way we started thinking about non-blocking IO ... </li> <li> WSPerfLab https://github.com/Netflix-Skunkworks/WSPerfLab </li> <li> WSPerfLab https://github.com/Netflix-Skunkworks/WSPerfLab Best case latency for response is ~155ms </li> <li> RxNetty Performance client: wrk -t 4 -c 400 server CPUs ~2% idle NOTE: It is still early days for RxNetty tuning; more to come </li> <li> Tuning for an acceptable latency distribution client: ab server CPU usage varies with load </li> <li> 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 </li> <li> 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. </li> <li> Approaching light speed... </li> <li> 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... </li> <li> Visualizing Tomcat Overheads Flame graph of Java CPU time for Hello World benchmark Thread and I/O management overheads </li> <li> 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 </li> <li> Blog post forthcoming Keywords: Oracle JDK 1.8.0 (others tried); Xmx &amp; 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, ... </li> <li> RxNetty &amp; Karyon 2.0 @NiteshKant </li> <li> 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 </li> <li> 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 </li> <li> We want an extremely performant IPC library Heard this before? </li> <li> Reinventing The Wheel? Image Courtesy: Oxford Creativity Finagle Vert.x Ratpack Tons of Netty based Frameworks? </li> <li> On the lookout .. Low level control. Traffic Shaping. (Zuul) Backpressure. Netflix fit Reactive programming model. Netflix Components (Eureka, Archaius, Governator, etc.) </li> <li> RxNetty Reactive Extensions To Netty Nucleus of Netflixs IPC stack Powering Ribbon 2.0 &amp; Karyon 2.0 https://github.com/Netflix/RxNetty </li> <li> Example (Server Sent Events) RxNetty.createHttpServer(8887, (request, response) -&gt; { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -&gt; { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.sseServerConfigurator()).start(); Server Client RxNetty.createHttpClient("127.0.0.1", 8887, PipelineConfigurators.sseClientConfigurator()) .submit(HttpClientRequest.createGet("/")) .flatMap(response -&gt; response.getContent()) .toBlocking().forEach(event -&gt; System.out.println(event)) </li> <li> data: interval: 0 data: interval: 1 data: interval: 2 data: interval: 3 Output ... data: interval: 4 data: interval: 5 </li> <li> Example (Stream Aggregation) RxNetty.createHttpServer(8887, (request, response) -&gt; { return Observable.interval(1, TimeUnit.SECONDS) .flatMap(interval -&gt; { return response.writeAndFlush(new ServerSentEvent(...)); }); }, PipelineConfigurators.sseServerConfigurator()).start(); { } *3 </li> <li> Example (Stream Aggregation) Observable.merge(RxNetty.createHttpClient("127.0.0.1", 8887...), RxNetty.createHttpClient("127.0.0.1", 8888...</li></ul>