netflixoss season 2 episode 2 - reactive / async

Post on 19-Aug-2014

2.156 Views

Category:

Engineering

4 Downloads

Preview:

Click to see full reader

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 2July 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<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);

});

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);

});

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);

});

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);

});

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);

});

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);

});

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);

});

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);

});

+

1.3.x (current production)1.4 Release Candidate (available now) - support for fully non-blocking1.4.0 Final (coming months)

0.19.x (current production)0.20 Release Candidates (coming weeks) - the “backpressure” release0.20.0 Final1.0 Release Candidates (Summer 2014) - no more breaking changes1.0.0 Final (Summer/Fall 2014)

github.com/Netflix/RxJava github.com/ReactiveX/RxJava

Version 1.x to be releasedon Maven Central with groupId io.reactivex.*

github.com/Netflix/RxJava github.com/ReactiveX/RxJava

github.com/ReactiveX/RxScalagithub.com/ReactiveX/RxGroovygithub.com/ReactiveX/RxClojuregithub.com/ReactiveX/RxKotlingithub.com/ReactiveX/RxJRubygithub.com/ReactiveX/RxAndroidetc ...

Version 1.x to be releasedon Maven Central with groupId io.reactivex.*

Along the way we started thinking about non-blocking IO ...

WSPerfLabhttps://github.com/Netflix-Skunkworks/WSPerfLab

WSPerfLabhttps://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 CPUusage varieswith load

RxNetty vs TomcatEvent 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 BenchmarkingServer: AWS c3.xlarge, tuned to reduce perturbationsClient: 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 <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...

Visualizing Tomcat Overheads● Flame graph of

Java CPU timefor Hello Worldbenchmark

● Thread and I/Omanagementoverheads

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 studiedof kernel and user-time

● Many improvements foundand 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)Apache

HTTP Client

Eureka (Service Registry)

Server (Karyon)

Apache

Tomcat

Client

Hystrix

EVCache

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

RxNetty

RxNetty

UDP

TCPWebSockets

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 Netflix’s 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.<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))

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.<ByteBuf>sseServerConfigurator()).start();{ } *3

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();

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<ByteBuf, ByteBuf> interceptorSupport) {

interceptorSupport.forUri("/hello").interceptIn(AuthInterceptor.class);

}

}

}

Ribbon 2Allen 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: Let’s 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 Examplepublic 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();

Ribbon 2: Template ExampleHttpResourceGroup 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 blockingByteBuf result = request.execute(); // blocking

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

Reactive outside Netflix

RetrofitJake Wharton

Retrofit

Example: GitHub API List Contributors

GET /repos/:owner/:repo/contributors

Retrofit

GET /repos/:owner/:repo/contributors

interface GitHub { List<Contributor> 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<Contributor> contributors( String owner, String repo);}

Retrofit

GET /repos/:owner/:repo/contributors

interface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

RetrofitRestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com/") .build();

GitHub gitHub = restAdapter.create(GitHub.class);

List<Contributor> contributors = gitHub.contributors("netflix", "rxjava");

RetrofitList<Contributor> contributors = gitHub.contributors("netflix", "rxjava");

for (Contributor c : contributors) { println(c.login + '\t' + c.contributions);}

1483 benjchristensen225 zsxwing167 samuelgruetter146 jmhofer137 akarnokd105 DavidMGross102 AppliedDuality...

RetrofitRestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .build();

Retrofitinterface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofitinterface GitHub { @GET("/repos/{owner}/{repo}/contributors?anon=true") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofitinterface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo, @Query("anon") boolean includeAnonymous);}

Retrofitinterface 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;}

RetrofitRestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://drive.google.com/api/") .setConverter(new ProtoConverter()) .setClient(new OkClient()) .build();

RetrofitOkHttpClient 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 + RxJavainterface GitHub { @GET("/repos/{owner}/{repo}/contributors") List<Contributor> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofit + RxJavainterface GitHub { @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo);}

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .forEach(c -> println(c.login + '\t' + c.contributions));

1483 benjchristensen225 zsxwing167 samuelgruetter146 jmhofer137 akarnokd105 DavidMGross102 AppliedDuality...

Retrofit + RxJavainterface 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);}

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.user(c.login)) .forEach(user -> println(user.name));

Ben ChristensenShixiong ZhunullJoachim HofernullDavid Grossnull...

Retrofit + RxJavagitHub.contributors("netflix", "rxjava") .lift(flattenList()) .flatMap(c -> gitHub.user(c.login)) .filter(user -> user.name != null) .forEach(user -> println(user.name));

Ben ChristensenShixiong ZhuJoachim HoferDavid GrossMatthias KäpplerJustin RyanMairbek Khadikov...

Retrofit + RxJavainterface 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);}

Retrofit + RxJavagitHub.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/RxJava2 twitter/finagle2 scala/scala2 mbostock/d32 kpelykh/docker-java2 Netflix/zuul2 Netflix/feign2 Netflix/archaius

Retrofit + RxJavagitHub.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-parcel4 Comcast/FreeFlow3 xxv/android-lifecycle3 robolectric/robolectric3 inmite/android-butterknife-zelezny3 google/auto3 facebook/rebound3 etsy/AndroidStaggeredGrid3 Netflix/RxJava

Retrofitsquare.github.io/retrofit

CouchbaseMatt 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<java.lang.Boolean> set(java.lang.String key, java.lang.Object value);

while (true) { client.set("foo", "bar").get() };

What we’re 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 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

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()); } }); } } });

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<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)); } })

CouchbaseReact to our Dev Preview 2!

Michael Nitschinger’s blog:http://blog.couchbase.com/couchbase-java-sdk-200-developer-preview-2

Five Minutes of FuturesWill 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 ? messageFuture 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 <- fooService.get(1)} yield { for { // Option / Future does not compose foo <- maybeFoo } yield ...}

Scala Async!

async { val maybeFoo = await(fooService.get(1)) maybeFoo.flatMap { foo => val bar = await(barService.get(2)) }}

Scala Async Sadness

COMPILE ERROR.

“await must not be used inside a closure nested within an async block” == no flatMap of that.

Monads don’t compose

But before reaching for readers/transformers:● Use small methods● Short circuit● Use loan patterns

Use Small Methods

def foo2bar(ff:Future[Foo]) : Future[Bar]

Short Circuit

implicit def flatten[A](fofoa: Future[Option[Future[Option[A]]]]): Future[Option[A]] = fofoa.flatMap(_.getOrElse(Future.successful(None)))

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) } }

What’s next?

Demos, food and drinks!

August 20th - NetflixOSS and Cloud Security

top related