building scalable stateless applications with rxjava
DESCRIPTION
RxJava is a lightweight open-source library, originally from Netflix, that makes it easy to compose asynchronous data sources and operations. This presentation is a high-level intro to this library and how it can fit into your application.TRANSCRIPT
![Page 1: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/1.jpg)
Building Scalable Stateless Applications with
RxJavaRick Warren | Principal Engineer, eHarmony [email protected] | @DangerLemming
![Page 2: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/2.jpg)
RxJava• Encapsulates data sequences: Observable
• Provides composable operations on them
• Abstracts away threading and synch
• Port from Microsoft .NET Reactive Extensions
• Java 6+, Groovy, Clojure, JRuby, Kotlin, and Scala; Android-compatible
![Page 3: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/3.jpg)
Netflix
Coarse-Grained API
Mi- -cro Ser- -vic- -es
ClientClient Client Client Client
Orchestration Layer
![Page 4: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/4.jpg)
ApplicabilityApplication or Presentation-Layer Service
Data Source
Data Source
Data Source
Data Source
Data Source
ClientClientClient Client Client
![Page 5: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/5.jpg)
3 Problems
1. Streaming queries
2. Rate-limited APIs (with retries)
3. Using Futures
![Page 6: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/6.jpg)
Data Store ClientService
Streaming Queries
1. Query lots of data from NoSQL store
2. Enrich, filter, and score on the fly
3. Deliver “best” results to client
1 2 3
![Page 7: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/7.jpg)
Streaming Queries
1Query
2Enrich
3Deliver
+ =
Too Much Latency
![Page 8: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/8.jpg)
Streaming Queries
&& =
1Query
2Enrich
3Deliver
More Efficient
![Page 9: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/9.jpg)
Data Store Client
Streaming QueriesIterables
ReadEnri
chSco
reDrai
n
• Iterator pattern for fast start and lower footprint • Composed as Decorators for flexibility • Limitation: Doesn’t abstract timing, concurrency
![Page 10: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/10.jpg)
Rate-Limited APIsScenario: Ingest data from public API—they will refuse rapid requests!
YouTube, Facebook,
etc.
Your Service
![Page 11: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/11.jpg)
Rate-Limited APIsYouTube,
Facebook, etc.
Your Service
Insight: The module responsible for what data is available is also responsible for when data is available
![Page 12: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/12.jpg)
Rate-Limited APIsYouTube,
Facebook, etc.
Your Service
Solution: Facade API with augmented Visitors to encapsulate timing and retries
interface DataVisitor<T> extends java.io.Closeable { void accept(T data); }
Limitation: Scheduling doesn't generalize
![Page 13: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/13.jpg)
Using Futures
• API and implementation cluttered with concurrency variants • Caller responsible for concurrency, but has least information
• What can you do with a Future besides block?
javax.ws.rs.client.Invocation
Response invoke()
Future<Response> submit()
![Page 14: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/14.jpg)
Using FuturesListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<Explosion> explosion = service.submit(new Callable<Explosion>() { public Explosion call() { return pushBigRedButton(); } }); Futures.addCallback(explosion, new FutureCallback<Explosion>() { public void onSuccess(Explosion explosion) { walkAwayFrom(explosion); } public void onFailure(Throwable thrown) { battleArchNemesis(); } });
What about Guava’s ListenableFuture?
• Requires access to ExecutorService! • Still hard to compose • Doesn’t generalize to multiple results
![Page 15: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/15.jpg)
Make ItWork
Interactions should: • Support multiple values • Be efficient & incremental • Encapsulate timing • Compose operations
![Page 16: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/16.jpg)
RxJava: Observable• Like Iterable, but offers fluent chaining operations
• Like Stream,but supports async termination and error handling
![Page 17: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/17.jpg)
Iterable Exampletry {
for (E elem : elements) { onNext(elem);
} onCompleted();
} catch (Throwable ex) { onError(ex);
}
![Page 18: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/18.jpg)
Iterable Exampletry {
elements.forEach(elem -‐> onNext(elem)
); onCompleted();
} catch (Throwable ex) { onError(ex);
}
First-class Visitor (Consumer)
![Page 19: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/19.jpg)
Stream Exampletry {
elements.parallelStream() .filter(condition) .map(transformation) .forEach(elem -‐> onNext(elem));
onCompleted(); } catch (Throwable ex) {
onError(ex); }
Still serial
Parallelize operations
![Page 20: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/20.jpg)
Observable Exampleelements
.filter(condition)
.map(transformation)
.subscribe( elem -‐> onNext(elem), ex -‐> onError(ex), () -‐> onCompleted());
Fully async’able
![Page 21: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/21.jpg)
Creating an Observableimport rx.*;
Observable<String> o = Observable.just( "a", "b", "c");
Iterable<String> it = ImmutableList.of( "a", "b", "c");Observable<String> o = Observable.from(it);
Future<String> f = exec.submit(myTask);Observable<String> o = Observable.from( f, Schedulers.from(exec));
![Page 22: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/22.jpg)
Example: JDBCpublic final class ObservableDB { private final DataSource db;
public ObservableDB(final DataSource source) { this.db = Objects.requireNonNull(source); }
/** Each emitted List represents a row in the ResultSet. */ public Observable<List<Object>> select( final String sql, final Iterable<?> params) { return Observable.create(new Observable.OnSubscribe<List<Object>>() { @Override public void call(final Subscriber<? super List<Object>> sub) { // ... } }); } }
![Page 23: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/23.jpg)
Example: JDBC@Override public void call(final Subscriber<? super List<Object>> sub) { try { try (Connection cx = db.getConnection(); PreparedStatement stmt = cx.prepareStatement(sql)) { int i = 0; for (final Object p : params) { stmt.setObject(i++, p); } try (ResultSet results = stmt.executeQuery()) { while (results.next() && !sub.isUnsubscribed()) { final Lists<Object> row = new ArrayList<>(); for (int col = 0; col < results.getMetaData().getColumnCount(); ++col) { row.add(results.getObject(col)); } sub.onNext(row); } } } if (!sub.isUnsubscribed()) { sub.onCompleted(); } } catch (final Exception ex) { if (!sub.isUnsubscribed()) { sub.onError(ex); } } }
Serialize
Call n times
Call one or the other, once
![Page 24: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/24.jpg)
Example: JDBCpublic void printProductCatalog( final DataSource source, final boolean awesome) { ObservableDB db = new ObservableDB(source); Subscription subscription = db.select( "SELECT * FROM products WHERE isAwesome=?", Collections.singleton(awesome)) // Func1<List<Object>, Product> : .map(unmarshallRowIntoProduct()) // Func1<Product, Observable<ProductWithPrice>> : .flatMap(remoteLookupPriceForProduct(this.priceService)) .take(NUM_PAGES * NUM_PRODUCTS_PER_PAGE) .window(NUM_PAGES) .subscribe(new Observer<Observable<ProductWithPrice>>() { ... }); // Some time later, if we change our minds: //subscription.unsubscribe(); }
interface PriceService { Observable<ProductWithPrice> getPrice(Product p); }
![Page 25: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/25.jpg)
Example: JDBC.subscribe(new Observer<Observable<ProductWithPrice>>() { private final AtomicInteger pageNumber = new AtomicInteger(1);
@Override public void onNext(final Observable<ProductWithPrice> page) { System.out.println("Page " + pageNumber.getAndIncrement()); page.forEach(new Action1<ProductWithPrice>() { @Override public void call(final ProductWithPrice product) { System.out.println("Product:" + product); } }); }
@Override public void onError(final Throwable ex) { System.err.println("This is how you handle errors? Srsly?"); }
@Override public void onCompleted() { System.out.println("Copyright 2014 ACME Catalog Company"); } });
![Page 26: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/26.jpg)
OperationsUseful stuff, built in
![Page 27: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/27.jpg)
Content Filtering• Observable<T> filter(Func1<T, Boolean> predicate)
• Observable<T> skip(int num)
• Observable<T> take(int num)
• Observable<T> takeLast(int num)
• Observable<T> elementAt(int index)
• Observable<T> distinct()
• Observable<T> distinctUntilChanged()
![Page 28: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/28.jpg)
Time Filtering• Observable<T> throttleFirst(long duration, TimeUnit unit)
• Observable<T> throttleLast(long duration, TimeUnit unit)
• Observable<T> timeout(long duration, TimeUnit unit)
![Page 29: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/29.jpg)
Transformation• Observable<R> map(Func1<T, R> func)
• Observable<R> flatMap(Func1<T, Observable<R>> func)
• Observable<R> cast(Class<R> klass)
• Observable<GroupedObservable<K, T>> groupBy(Func1<T, K> keySelector)
![Page 30: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/30.jpg)
ConcurrencyEncapsulated, so usually you don’t care
![Page 31: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/31.jpg)
Concurrency1. Single-threaded and synchronous by default:
RxJava doesn’t magically create new threads for you
2. When creating an Observable, invoke Subscriber from any thread you like (or use Actors, etc.)
3. Derive new Observables by binding subscription and/or observation to Schedulers
![Page 32: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/32.jpg)
Rescheduling
Observable<T> rescheduled = obs .subscribeOn(mySubScheduler) .observeOn(myObsScheduler);
![Page 33: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/33.jpg)
What’s a Scheduler?
Policy for time-sharing among Workers
• Worker: Serial collection of scheduled Actions
• Action: Callable unit of work, e.g. Observable’s subscription or notification
![Page 34: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/34.jpg)
Built-in Schedulers• Immediate—Schedulers.immediate()
• Run without scheduling in calling thread
• Trampoline—Schedulers.trampoline() • Enqueue Actions in thread-local priority queue
• Computation—Schedulers.computation() • Enqueue in event loop with as many threads as cores
![Page 35: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/35.jpg)
Built-in Schedulers• New Thread—Schedulers.newThread()
• Each Worker is a single-thread ExecutorService
• I/O—Schedulers.io() • Mostly like New Thread, but with some pooling
• Executor Service—Schedulers.from( ExecutorService)• Bind new Scheduler to arbitrary ExecutorService, with
external serialization of Actions. Observations may hop threads!
![Page 36: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/36.jpg)
Bring Data to Calling Thread
myObservable.toBlocking()
Avoid whenever possible
Operations: • first() • last() • toFuture() • toIterable()
![Page 37: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/37.jpg)
Learn More:• Wiki: https://github.com/ReactiveX/RxJava/wiki
• JavaDoc: http://reactivex.io/RxJava/javadoc/
![Page 38: Building Scalable Stateless Applications with RxJava](https://reader031.vdocuments.mx/reader031/viewer/2022013115/5599f03a1a28abea1a8b472b/html5/thumbnails/38.jpg)
More Resources• Netflix API architecture: http://
techblog.netflix.com/2013/01/optimizing-netflix-api.html
• Jersey: https://jersey.java.net/apidocs/latest/jersey/index.html
• Guava ListenableFuture: https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained
• rxjava-jdbc: https://github.com/davidmoten/rxjava-jdbc/
• Email: [email protected]
• Twitter: @DangerLemming
• GitHub: https://github.com/rickbw
• LinkedIn: https://www.linkedin.com/in/rickbwarren