Date post: | 21-Jan-2018 |
Category: |
Technology |
Upload: | yakov-fain |
View: | 1,639 times |
Download: | 0 times |
About myself• Solutions Architect at Farata Systems
• Java Champion
• Latest book:“Angular 2 Development with TypeScript”
Synchronous vs asynchronous
• Synchronous programming is straightforward
• Asynchronous programming dramatically increases complexity
Challenges in async programming
• Handling failed async requests
• Chaining async calls
• The user already closed the view
• Switching threads to update UI
and more challenges
• Avoiding writing blocking code in a multi-threaded app
• Several consumers
• Composable functions
• Killing a pending Http request
Backpressure
SubscriberSammy
A publisher generates more data than a subscriber can process
Publisher
Examples of backpressure
• The stock price changes 100 times a second
• Accelerometer produces 50 signals a 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.
Reactive Apps
• The data is processed as streams
• Message-driven: notifications
• Resilient: stay alive in case of failures
• Data flows through your app’s algorithms
• Hide complexity of multi-threading
Reactive Streams
• 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 Stream, MongoDB, Vert.x …
Main RxJava2 players
• Observable or Flowable - producers of data
• Observer or Subscriber - consumers of data
• Subject or Processor - implements both 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 RxJava2Publisher Subscriber
Creating an Observable• Observable.create()
• Observable.fromArray()
• Observable.fromIterable()
• Observable.fromCallable()
• Observable.fromFuture()
• Observable.range()
• Observable.just()
Observable.create(subscriber -> {
int totalBeers = beerStock.size(); for (int i = 0; i < totalBeers; i++) {
// synchronous push subscriber.onNext(beerStock.get(i));
}
subscriber.onComplete(); });
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!!!”) ););
Observable push
Observer
Subscription
Creating an Observer and subscribingObservable<Beer> beerData = BeerServer.getData(); // returns an 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
Subscription subscription = Observable.create(new Observable.OnSubscribe<Response>() {
OkHttpClient client = new OkHttpClient(); @Override public void call(Subscriber<? super Response> subscriber) { // invoked on subscription try { Response response = client.newCall( // prepare the call for future execution
new Request.Builder().url(“http://localhost:8080/beers“) .build()) .execute(); // use enqueue() for async requests subscriber.onNext(response); subscriber.onComplete(); if (!response.isSuccessful()) { subscriber.onError(new Exception(“Can’t get beers”)); } } catch (IOException e) { subscriber.onError(e); } } }) .subscribe(...); // pass Observer; use observeOn/SubscribeOn to switch threads
Pushing HTTP responses
https://square.github.io/okhttp
Handling backpressure
Publisher Subscriber
request(1)
request(3)
…
request() is non-blocking
onNext(value1)
onNext(value2)
onNext(value3)
onNext(value4)
Creating a Flowable• Flowable.create()
• Flowable.fromArray()
• Flowable.fromIterable()
• Flowable.fromCallable()
• Flowable.empty()
• Flowable.range()
• Flowable.just()
Flowable.create() and Observable.toFlowable()
myObservable .toFlowable(BackpressureStrategy.BUFFER);
Flowable<Beer> myFlowable .create(beerEmitter ->{…}, BackpressureStrategy.BUFFER);
Create
Convert from Observable
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 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) { // just to demo unsubscribing 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); }}
Demo 1. FlowableRange
2. BeerClientFlowable
Code samples: https://github.com/yfain/rxjava2
An Operator
Observable Observable
A transforming function
observableBeer .filter(b -> "USA".equals(b.country))
Docs: http://reactivex.io/documentation/operators
QUIZ: What value(s) this observable emits?
Observable .just(5, 9, 10) // emits 5, 9, 10 .filter(i -> i % 3 > 0) .map(n -> "$" + n * 10) .filter(s -> s.length() < 4);
Observable .just(5, 9, 10) // emits 5, 9, 10 .filter(i -> i % 3 > 0) .map(n -> "$" + n * 10) .filter(s -> s.length() < 4) .subscribe(System.out::println);
Types of ObservablesHot Cold
Push
Produce a steam even if no-one cares
rProduce a stream when
someone asks for it✔
Turning a cold observable into hot
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 ));
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
• Schedulers.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
Spawn a separate thread for 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
Switching to the main thread Updating the UI
Parallel operations with ParallelFlowabe
• Parallel execution of some operators
• The source sequence is dispatched into N parallel “rails”
• runOn() —-> sequential() is more efficient fork/join than flatMap()
• Each rail can spawn multiple async threads with Schedulers
Parallel Execution
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);
Tasks run simultaneously on different CPUs or computers.
ParallelFlowableRange.java
Summary• Observable: no backpressure support
• Flowable: backpressure support
• Operators can be chained
• flatmap() used for handling observable of observables
• Schedulers support multi-threading
• subscribeOn()/observeOn() - switching between threads
• ParallelFlowable - initiate parallel processing
Links• Slides: http://bit.ly/2q3Ovt4
• Code samples: https://github.com/yfain/rxjava2
• Our company: faratasystems.com
• Blog: yakovfain.com
• Twitter: @yfain