reactive thinking in java with rxjava2

Post on 12-Apr-2017

298 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Reactive Thinking in Java with RxJava2

Yakov Fain, Farata Systems

@yfain

About myself• Solutions Architect at Farata Systems

• Java Champion

• Latest book: “Angular 2 Development with TypeScript”

The problem with not being reactive

int a1 = 2; int b1 = 4; int c1 = a1 + b1; // c1 = 6

int a1 = 2; int b1 = 4; int c1 = a1 + b1; // c1 = 6 a1 = 55; // c1 = 6; b1 = 20; // c1 = 6;

The problem with not being reactive

A famous reactive solution

But we face more challenges• An async request to a server failed later

• You need to chain multiple async calls

• An async response came back, but the user already moved to a different view

• An async request was executed on a thread A, but the UI must updated on a main thread

• Allocating a thread to each async request is expensive

• How to avoid blocking code in a multi-threaded app?

and even more challenges

• The same stream has to be processed by several consumers

• The producer pushes the data that should flow through several composable functions

• Mobile device. Slow connection. An Http request is pending, but the user made another one. How to kill the pending request?

• The publisher generates data faster than a consumer can handle

Backpressure

Publisher Subscriber

Publisher generates more data than subscriber can (or want) process

Reactive Apps• The data is processed as streams and not via iterating over

the in-memory data

• Message-driven: components communicate via direct notifications

• A stream can be handled by one or more composable operators (functions).

• Resilient: stay responsive in case of failures

• The data flows through your app’s algorithm

• Hide complexity of multi-threading

Reactive Streams• Reactive Streams is a spec for async stream processing with non-

blocking backpressure http://www.reactive-streams.org

• Reactive Streams interfaces for JVM https://github.com/reactive-streams/reactive-streams-jvm

• Reactive Streams-based products: RxJava2, Java 9 Flow APIs, Reactor 3, Akka, MongoDB, Vert.x …

Reactive Streams: Java interfaces

1

2

3

4

backpressuresupport

Examples of backpressure• The stock price may change hundreds times a second, but the

user’s display should change once a second.

• Android accelerometer produces 50 readings a second, but your app should react to one signal per second.

• Iteration through a JDBC result set (rxjava-jdbc; rxjava2-jdbc)

• A user drags the mouse to draw a curve. Can’t apply back pressure.

Handling backpressure

Publisher Subscriber

request(1)

request(3)

Even if the publisher is slow and the data is not be available, the request() doesn’t block.

onNext(value1)

onNext(value2)

onNext(value3)

onNext(value4)

Rx libraries

• RxJava (end of life: March 2018) RxAndroid, RxJavaFX, RxSwing

• RxJava2

• Other Rx libraries: Rx.NET, RxCpp, RxJS, Rx.rb, Rx.py, RxSwift, RxScala, RxPHP

http://reactivex.io

Main RxJava2 players• Observable or Flowable - producers of data

• Observer or Subscriber - consumers of data

• Subject or Processor - implements producer and consumer

• Operator - en-route data transformation

• Scheduler - multi-threading support

Main Publishers and Subscribers in RxJava2

Observable no backpressure support

public interface Observer<T> { void onSubscribe(Disposable var1); void onNext(T var1); void onError(Throwable var1); void onComplete();}

Publisher Subscriber

Not from Reactive Streams

Observable no backpressure support

Flowable with backpressure support

public interface Observer<T> { void onSubscribe(Disposable var1); void onNext(T var1); void onError(Throwable var1); void onComplete();}

public interface Subscriber<T> { void onSubscribe(Subscription var1); void onNext(T var1); void onError(Throwable var1); void onComplete();}

Not from Reactive Streams

From Reactive Streams

Main Publishers and Subscribers in RxJava2

Publisher Subscriber

beers.forEach(beer -> { if ("USA".equals(beer.country)){ americanBeers.add(beer); } });

Java Iterable: a pull

beers.stream() .skip(1) .limit(3) .filter(beer -> "USA".equals(beer.country)) .map(beer -> beer.name + ": $" + beer.price) .forEach(beer -> System.out.println(beer));

Java 8 Stream API: a pull

A pull with a tool is still a pull

Observable<Beer> observableBeer = Observable.create(/* data source */);

observableBeer .skip(1) .take(3) .filter(beer -> "USA".equals(beer.country)) .map(beer -> beer.name + ": $" + beer.price) .subscribe( beer -> System.out.println(beer), err -> System.out.println(err), () -> System.out.println("Streaming is complete”), disposable -> System.out.println( "Someone just subscribed to the beer stream!!!”) ););

Rx Observable: a push

Observer

Subscribtion

Adding RxJava2 to your project

Or find rxjava2 and reactive-streams jars on search.maven.org

<dependency> <groupId>io.reactivex.rxjava2</groupId> <artifactId>rxjava</artifactId> <version>x.y.z</version> </dependency>

Maven:

Creating an Observable• Observable.create()

• Observable.fromArray()

• Observable.fromIterable()

• Observable.fromCallable()

• Observable.fromFuture()

• Observable.range()

• Observable.just()

Synchronous pushList<Beer> beerStock = new ArrayList<>(); …

Observable.create(subscriber -> { int totalBeers = beerStock.size(); for (int i = 0; i < totalBeers; i++) { subscriber.onNext(beerStock.get(i)); } subscriber.onComplete();

Observable.create(subscriber -> { myHttpClient.getBeers(new Callback(){

public void onResponse(Response res){ subscriber.onNext(res.body().string()); subscriber.onComplete(); } public void onFailure (IOException e){ subscriber.onError(e); } } });

Asynchronous push

Creating an Observer and subscribingObservable<Beer> beerData = BeerServer.getData(); // returns ObservableObserver beerObserver = new Observer<Beer>() { public void onSubscribe(Disposable d) { System.out.println( " !!! Someone just subscribed to the bear stream!!! "); // If the subscriber is less than 21 year old, cancel subscription // d.dispose(); } public void onNext(Beer beer) { System.out.println(beer); } public void onError(Throwable throwable) { System.err.println("In Observer.onError(): " + throwable.getMessage()); } public void onComplete() { System.out.println("*** The stream is over ***"); }}; beerData .subscribe(beerObserver); // Streaming starts here

Demo BeerClient

Specialized Observables• Single - Emits a exactly one item or sends an error

• Completable - Emits either complete or error - no data Any response is better than no response!

• Maybe - Either emits exactly one item, or completes with no items, or sends an error

Controlling the flow with request()

request(1); request(1);

Flowables and backpressure strategies• BackpressureStrategy.BUFFER - process what you can; put the rest in the buffer

until the next request.

• BackpressureStrategy.DROP - process what you can; ignore the rest until the next request.

• BackpressureStrategy.LATEST - process what you can; ignore the rest until the next request, but cache the latest element

• BackpressureStrategy.MISSING - don’t apply backpressure; if consumer can’t keep up, it may throw MissingBackpressureException or IllegalStateException

• BackpressureStrategy.ERROR - apply backpressure; if consumer can’t keep up, it throws MissingBackpressureException

The BUFFER strategy

BackpressureStrategy.BUFFER

BackpressureStrategy.DROP

The DROP strategy

BackpressureStrategy.LATEST

The LATEST strategy

Creating a Flowable• Flowable.create()

• Flowable.fromArray()

• Flowable.fromIterable()

• Flowable.fromCallable()

• Flowable.empty()

• Flowable.range()

• Flowable.just()

Creating a Flowable• Flowable.create()

• Flowable.fromArray()

• Flowable.fromIterable()

• Flowable.fromCallable()

• Flowable.fromFuture()

• Flowable.empty()

• Flowable.range()

• Flowable.just()

myObservable .toFlowable(BackpressureStrategy.BUFFER)

Flowable<Beer> myFlowable .create(beerEmitter ->{…}, BackpressureStrategy.BUFFER)

Create

Convert

Requesting data from Flowablepublic class FlowableRange { static DisposableSubscriber<Integer> subscriber; public static void main(String[] args) { subscriber = new DisposableSubscriber<Integer>() { public void onStart() { request(5); while (true){ // Emulate some 1-sec processing try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } request(1); } } public void onNext(Integer t) { System.out.println("processing "+ t); if (t==8) { subscriber.dispose(); } } public void onError(Throwable thr) { System.err.println("In onError(): " + thr.getMessage()); } public void onComplete() { System.out.println("Done"); } }; Flowable.range(1, 10) .subscribe(subscriber); }}

Demos 1. FlowableRange

2. BeerClientFlowable

FP: a pure function• Produces no side effects

• The same input always results in the same output

• Doesn’t modify the input

• Doesn’t rely on the external state

Sharing state require locks, may cause race conditions, and complicates programming

FP: a higher-order function

• Can take one or more functions as argument(s)

• Can return a function

An Operator

Observable Observable

A transforming function

An Operator

Observable Observable

A transforming function

observableBeer .filter(b -> "USA".equals(b.country))

An Operator

Observable Observable

A transforming function

A higher-order function

observableBeer .filter(b -> "USA".equals(b.country))

A pure function

map

filter

RX: the data moves across your algorithm

Observable

Operator

Observable

Operator

ObservableOperator

ObservableOperator

QUIZ: What value(s) this observable will emit?

Observable .just(5, 9, 10) .filter(i -> i % 3 > 0) .map(n -> "$" + n * 10) .filter(s -> s.length() < 4);

Observable .just(5, 9, 10) .filter(i -> i % 3 > 0) .map(n -> "$" + n * 10) .filter(s -> s.length() < 4) .subscribe(System.out::println);

QUIZ: What value(s) this observable will emit?

Functions with side effects

• doOnNext()

• doOnError()

• doOnComplete()

• doOnEach()

• doOnSubscribe()

• doOnDispose()

Affect environment outside the function.

Error reportingObserver Observable

onNext()

onError()

onComplete()

When the Observable or Flowable throws an exception it still invokes Observer.onError() or Subscriber.onError()

Error-handling operators• onError() kills the subscription

• retryWhen() - intercept and analyze the error; resubscribe

• retryUntil() - retry until a certain condition is true

• onErrorResumeNext() - used for failover to another Observable

Demo BeerClientWithFailover

Composing observables

concat: combining observables of the same type in order

merge: combining observables of the same type

zip: combining observables of different types

flatMap

.flatMap()Observable

Can spawn multiple threads

Demo composingObservables/ObservableDrinks

switchMap

Demo Angular client, weather app

Schedulers

Concurrency with Schedulers

• subscribeOn(strategy) - run Observable in a separate thread: Operations with side effects like files I/O; Don’t hold off the UI thread

• observeOn(strategy) - run Observer in a separate thread, Update Swing UI on the Event Dispatch Thread Update Android UI on the Main thread

Multi-threading strategies• Schedulers.computation() - for computations: # of threads <= # of cores

• Schedulers.io() - for long running communications; backed by a thread pool

• Schedulers.newThread() - new thread for each unit of work

• Schedulers.from(Executor) - a wrapper for Java Executor

• Scedulers.trampoline() - queues the work on the current thread

• AndroidSchedulers.mainThread() - handle data on the main thread (RxAndroid)

Switching threads

Operator1() Operator2() ObserveOn()Observable

Subscriber

Thread 1

Thread 2

subscribeOn()

observeOn()

Multi-threaded processing with flatMap()

Operator1() Operator2() flatMap()Observable

Subscriber

Thread 1

Observable/Thr2

Observable/Thr3

Observable/ThrN

With flatMap() it’s easy to spawn a different thread to each observable

Turning a value into an observable

Observable.range(1, 1000) .flatMap(n->Observable.just(n) .subscribeOn(Schedulers.computation())) .map(n->n*2) .observeOn(AndroidSchedulers.mainThread()) .subscribe(myView.update());

Subscribing to each observable on the

computation thread

Displaying the result on the main thread

Demo schedulers/SubscribeOnObserveOn

ParallelStreamsRange ParallelStreams

Parallel operations with ParallelFlowabe

• ParallelFlowable allows parallel execution of a few operators

• The source sequence is dispatched into N parallel “rails”

• More efficient forking and joining than with flatMap() runOn() —-> sequential()

• Each rail can spawn multiple async threads with Schedulers

Parallel operations with ParallelFlowabe

int numberOfRails = 4; // can query #processors with parallelism()ParallelFlowable .from(Flowable.range(1, 10), numberOfRails) .runOn(Schedulers.computation()) .map(i -> i * i) .filter(i -> i % 3 == 0) .sequential() .subscribe(System.out::println);

Demo: ParallelFlowableRange

Types of ObservablesHot Cold

Push

Produce the steam even if no-one cares

rProduce the stream when

someone asks for it✔

Hot Observables• Mouse-generated events are emitted even if there are no subscribers

• Button clicks

• Stock prices are being published on a server socket regardless if any client is connected

• A sensor in a mobile device sends signals even if no apps are handling them

Making observables hot

Using publish() and connect()

ConnectableObservable<Long> numbers = (ConnectableObservable<Long>) Observable .interval(1, TimeUnit.SECONDS) // generate seq numbers every second .publish(); // make it hotnumbers.connect(); // creates an internal subscriber to start producing data

numbers.subscribe(n ->System.out.println("First subscriber: "+ n ));Thread.sleep(3000);numbers.subscribe(n ->System.out.println(" Second subscriber: "+ n ));

Demo HotObservable

Summary• Observable: no backpressue; no reactive streams spec

• Flowable: backpressure; reactive streams spec

• Operators can be chained

• flatmap() used for handling observable of observables

• Schedulers support multi-threading

• subscribeOn()/observeOn() - switching between threads

• ParallelFlowable - initiate parallel processing

• Observables can be hot or cold

Thank you!• Code samples:

https://github.com/yfain/rxjava2

• Our company: faratasystems.com

• My blog: yakovfain.com

• Twitter: @yfain

top related