RxJava Applied:
Concise Examples where It Shines
Igor Lozynskyi
JEEConf - May 20-21, 2016
Software Engineer at GlobalLogic
Presentation’s home
https://github.com/aigor/rx-presentation
Modern apps
Created with draw.io
Options to deal with integration complexity
JDeferred
CompletableFuture<T>
Scala.Rx
Scala Actors
RxJava
RxJava
http://reactivex.io
https://github.com/ReactiveX/RxJava
Short history
● From 2009 for .NET
● From 2013 for JVM (latest: 1.1.5, May 5, 2016)
● Now a lot of other languages
Use case: Stream of tweets
Created with Balsamiq Mockups
Twitter API
Twitter Stream API (WebSocket alike):
● Doc: https://dev.twitter.com/streaming/overview
● Library: com.twitter:hbc-core:2.2.0
Twitter REST API:
● GET https://api.twitter.com/1.1/users/show.json?screen_name=jeeconf
● GET https://api.twitter.com/1.1/search/tweets.json?q=from:jeeconf
Observer
interface Observer<T> {
void onNext(T t);
void onCompleted();
void onError(Throwable e);
}
Observable
.create(s -> {
s.onNext("A");
s.onNext("B");
s.onCompleted();
})
.subscribe(m -> log.info("Message received: " + m),
e -> log.warning("Error: " + e.getMessage()),
() -> log.info("Done!"));
Output:
Message received: A
Message received: B
Done!
Let’s look at entities
class Tweet {
String text;
int favorite_count;
String author;
int author_followers;
}
class Profile {
String screen_name;
String name;
String location;
int statuses_count;
int followers_count;
}
class UserWithTweet {
Profile profile;
Tweet tweet;
}
Marble diagram
Profile getUserProfile(String screenName) {
ObjectMapper om = new ObjectMapper();
return (Profile) om.readValue(om.readTree(
Unirest.get(API_BASE_URL + "users/show.json")
.queryString("screen_name", screenName)
.header("Authorization", bearerAuth(authToken.get()))
.asString()
.getBody()),
Profile.class);
}
Get user profile synchronously
Get user profile asynchronously
Observable<Profile> getUserProfile(String screenName) {
return Observable.fromCallable(() -> {
ObjectMapper om = new ObjectMapper();
return (Profile) om.readValue(om.readTree(
Unirest.get(API_BASE_URL + "users/show.json")
.queryString("screen_name", screenName)
.header("Authorization", bearerAuth(authToken.get()))
.asString()
.getBody()),
Profile.class);
});
}
Add some errors handling
Observable<Profile> getUserProfile(String screenName) {
if (authToken.isPresent()) {
return Observable.fromCallable(() -> {
ObjectMapper om = new ObjectMapper();
return (Profile) om.readValue(om.readTree(
Unirest.get(API_BASE_URL + "users/show.json")
.queryString("screen_name", screenName)
.header("Authorization", bearerAuth(authToken.get()))
.asString()
.getBody()),
Profile.class);
}).doOnCompleted(() -> log("getUserProfile completed for: " + screenName));
} else {
return Observable.error(new RuntimeException("Can not connect to twitter"));
}
}
Let’s do something concurrently
Illustration: Arsenal Firearms S.r.l.
Get data concurrently
Observable<UserWithTweet> getUserAndPopularTweet(String author){
return Observable.just(author)
.flatMap(u -> {
Observable<Profile> profile = client.getUserProfile(u)
.subscribeOn(Schedulers.io());
Observable<Tweet> tweet = client.getUserRecentTweets(u)
.defaultIfEmpty(null)
.reduce((t1, t2) ->
t1.retweet_count > t2.retweet_count ? t1 : t2)
.subscribeOn(Schedulers.io());
return Observable.zip(profile, tweet, UserWithTweet::new);
});
}
Let’s subscribe on stream of tweets!
streamClient.getStream("RxJava", "JEEConf", "Java")
streamClient.getStream("RxJava", "JEEConf", "Java", "Trump")
streamClient.getStream("RxJava", "JEEConf", "Java", "Trump")
.distinctUntilChanged()
.map(p -> p.author)
.flatMap(name -> getUserAndPopularTweet(name))
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.immediate())
.subscribe(p -> log.info("The most popular tweet of user "
+ p.profile.name + ": " + p.tweet));
.scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2)
streamClient.getStream("RxJava", "JEEConf", "Java", "Trump")
.scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2)
.distinctUntilChanged()
.map(p -> p.author)
.flatMap(name -> {
Observable<Profile> profile = client.getUserProfile(name)
.subscribeOn(Schedulers.io());
Observable<Tweet> tweet = client.getUserRecentTweets(name)
.defaultIfEmpty(null)
.reduce((t1, t2) ->
t1.retweet_count > t2.retweet_count ? t1 : t2)
.subscribeOn(Schedulers.io());
return Observable.zip(profile, tweet, UserWithTweet::new);
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.immediate())
.subscribe(p -> log.info("The most popular tweet of user "
+ p.profile.name + ": " + p.tweet));
Marble diagram
▸ API is big (150+ methods to remember)
▸ Requires to understand underlying magic
▸ Hard to debug
▸ Don’t forget about back pressure
Conclusions: pitfalls
▸ It is functional, it is reactive*
▸ Good for integration scenarios
▸ Allows to control execution threads
▸ Easy to compose workflows
▸ Easy to integrate into existing solutions
▸ Easy to test
* RxJava is inspired by FRP (Functional Reactive Programming), but doesn’t implement it
Conclusions: strength
Q&A
Thanks
Presentation template by SlidesCarnival
@siromaha
aigooor@gmail.com

RxJava Applied

  • 1.
    RxJava Applied: Concise Exampleswhere It Shines Igor Lozynskyi JEEConf - May 20-21, 2016 Software Engineer at GlobalLogic
  • 2.
  • 3.
  • 4.
    Options to dealwith integration complexity JDeferred CompletableFuture<T> Scala.Rx Scala Actors RxJava
  • 5.
  • 6.
    Short history ● From2009 for .NET ● From 2013 for JVM (latest: 1.1.5, May 5, 2016) ● Now a lot of other languages
  • 7.
    Use case: Streamof tweets Created with Balsamiq Mockups
  • 8.
    Twitter API Twitter StreamAPI (WebSocket alike): ● Doc: https://dev.twitter.com/streaming/overview ● Library: com.twitter:hbc-core:2.2.0 Twitter REST API: ● GET https://api.twitter.com/1.1/users/show.json?screen_name=jeeconf ● GET https://api.twitter.com/1.1/search/tweets.json?q=from:jeeconf
  • 9.
    Observer interface Observer<T> { voidonNext(T t); void onCompleted(); void onError(Throwable e); }
  • 10.
    Observable .create(s -> { s.onNext("A"); s.onNext("B"); s.onCompleted(); }) .subscribe(m-> log.info("Message received: " + m), e -> log.warning("Error: " + e.getMessage()), () -> log.info("Done!")); Output: Message received: A Message received: B Done!
  • 11.
    Let’s look atentities class Tweet { String text; int favorite_count; String author; int author_followers; } class Profile { String screen_name; String name; String location; int statuses_count; int followers_count; } class UserWithTweet { Profile profile; Tweet tweet; }
  • 12.
  • 13.
    Profile getUserProfile(String screenName){ ObjectMapper om = new ObjectMapper(); return (Profile) om.readValue(om.readTree( Unirest.get(API_BASE_URL + "users/show.json") .queryString("screen_name", screenName) .header("Authorization", bearerAuth(authToken.get())) .asString() .getBody()), Profile.class); } Get user profile synchronously
  • 14.
    Get user profileasynchronously Observable<Profile> getUserProfile(String screenName) { return Observable.fromCallable(() -> { ObjectMapper om = new ObjectMapper(); return (Profile) om.readValue(om.readTree( Unirest.get(API_BASE_URL + "users/show.json") .queryString("screen_name", screenName) .header("Authorization", bearerAuth(authToken.get())) .asString() .getBody()), Profile.class); }); }
  • 15.
    Add some errorshandling Observable<Profile> getUserProfile(String screenName) { if (authToken.isPresent()) { return Observable.fromCallable(() -> { ObjectMapper om = new ObjectMapper(); return (Profile) om.readValue(om.readTree( Unirest.get(API_BASE_URL + "users/show.json") .queryString("screen_name", screenName) .header("Authorization", bearerAuth(authToken.get())) .asString() .getBody()), Profile.class); }).doOnCompleted(() -> log("getUserProfile completed for: " + screenName)); } else { return Observable.error(new RuntimeException("Can not connect to twitter")); } }
  • 16.
    Let’s do somethingconcurrently Illustration: Arsenal Firearms S.r.l.
  • 18.
    Get data concurrently Observable<UserWithTweet>getUserAndPopularTweet(String author){ return Observable.just(author) .flatMap(u -> { Observable<Profile> profile = client.getUserProfile(u) .subscribeOn(Schedulers.io()); Observable<Tweet> tweet = client.getUserRecentTweets(u) .defaultIfEmpty(null) .reduce((t1, t2) -> t1.retweet_count > t2.retweet_count ? t1 : t2) .subscribeOn(Schedulers.io()); return Observable.zip(profile, tweet, UserWithTweet::new); }); }
  • 20.
    Let’s subscribe onstream of tweets!
  • 21.
  • 22.
  • 23.
    streamClient.getStream("RxJava", "JEEConf", "Java","Trump") .distinctUntilChanged() .map(p -> p.author) .flatMap(name -> getUserAndPopularTweet(name)) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.immediate()) .subscribe(p -> log.info("The most popular tweet of user " + p.profile.name + ": " + p.tweet)); .scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2)
  • 24.
    streamClient.getStream("RxJava", "JEEConf", "Java","Trump") .scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2) .distinctUntilChanged() .map(p -> p.author) .flatMap(name -> { Observable<Profile> profile = client.getUserProfile(name) .subscribeOn(Schedulers.io()); Observable<Tweet> tweet = client.getUserRecentTweets(name) .defaultIfEmpty(null) .reduce((t1, t2) -> t1.retweet_count > t2.retweet_count ? t1 : t2) .subscribeOn(Schedulers.io()); return Observable.zip(profile, tweet, UserWithTweet::new); }) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.immediate()) .subscribe(p -> log.info("The most popular tweet of user " + p.profile.name + ": " + p.tweet));
  • 25.
  • 26.
    ▸ API isbig (150+ methods to remember) ▸ Requires to understand underlying magic ▸ Hard to debug ▸ Don’t forget about back pressure Conclusions: pitfalls
  • 27.
    ▸ It isfunctional, it is reactive* ▸ Good for integration scenarios ▸ Allows to control execution threads ▸ Easy to compose workflows ▸ Easy to integrate into existing solutions ▸ Easy to test * RxJava is inspired by FRP (Functional Reactive Programming), but doesn’t implement it Conclusions: strength
  • 29.
  • 30.
    Thanks Presentation template bySlidesCarnival @siromaha aigooor@gmail.com

Editor's Notes

  • #2 Добрий день Звати Ігор Сьогодні за 15 ха розкажу про RxJava ТУТ ЛІНК НА ПРЕЗЕНТАШКУ
  • #3 Тим часом: GL Працюю з Java 7 років МЕТА: заціквити, підштовхнути до Реактивщини Ще одне: прошу НЕНАГАЛЬНІ питання запам’ятовувати, їх ми обговоримо вкінці.
  • #4 Спрощено виглядає сучасна апа БАГАТО: даних, клієнтів, різних API, зовнішніх сервісів як це менеджити?
  • #5 ДЛЯ ЦЬОГО Є КУПА РІШЕНЬ НАСПРАВДІ ЇХ БІЛЬШЕ цікавить - цей ЗВІРЬОК
  • #6 Хтось працював з RxJava? З іншими РЕАКТИВНИМИ системами?
  • #7 Ідея не нова В ТАБОРІ ВОРОГІВ з 2009 Потім був порт на JS Завдяки БЕНУ КРІСТІНСЕНУ з Netflix Є порти під інші мови
  • #8 Спробуємо реалізувати таке Зверху рядок слів, які нас цікавлять в твітах Зліва - потік цих твітів Справа - топ автори і їх топ твіти Трохи штучно, для демо згодиться
  • #9 БУДЕМО ВИКОРИСТОВУВАТИ Stream API - бібліотека від twitter 2 REST запита: профіль і недавні твіти
  • #10 Коротко - PUSH ільтернатива Java 8 Stream API Дуже схожі ГЛЯНЕМО потік твітів -> популярний твіт популярного автора
  • #11 Коротко - PUSH ільтернатива Java 8 Stream API Дуже схожі ГЛЯНЕМО потік твітів -> популярний твіт популярного автора
  • #12 Доменні об’єкти ТВІТ (+ деяка інфа про автора) ПРОФІЛЬ (зовсім кілька атрибутів) КОНТЕЙНЕР - тримати разом ганятимемо через Observable потоки
  • #13 Marble Diagrams - ВІЗУАЛІЗАЦІЯ 1. Просто потік твітів (+ автор) 2. Порівнюємо з допоки найпопюлярнішим автором і замінюємо популярнішим 3. Фільтруємо унікальні 4. Залишимо лише ім’я автора 5. По імені -> профіль + список остінніх твітів (REST API) 6. список остінніх твітів -> популярний твіт (reduce) 7. ПАРАЛЕЛЬНО 8. Зібрати дані повенути як тільки готові дочірні 9. FLATMAP -> верне результат 10. СКЛАДНО? 11. Почнем з простого - витягнемо профіль
  • #14 Цей код робочий)) АСИНХРОННО?
  • #15 ЩО ЗМІНИЛОСЬ? При виклику методу код не виконається - РЕЄСТРАЦІЯ дії
  • #16 1. Обробка помилок 2. Додаткова дія коли цей потік закінчиться Note: рішення про помилку робиться в момент виклику методу! Нічого складного?
  • #17 Ну що, зробимо паралельно? До речі такий пістолет дійно існує: Італійська конпанія Arsenal Firearms з 2011 року робить такі іграшки При чому в кількох калібрах)))
  • #18 Ми бачили як дістати ПРОФІЛЬ Припустимо що так само - ТВІТИ Тре зрозуміти як зробити це паралельно
  • #19 Note: перший твіт, а не найпопулярніший ВИПРАВИМО
  • #20 Ось цю частину ми реалізували Спробує опанувати решту
  • #21 Для цього підпишимось на ПОТІК
  • #22 Нам треба: 1. Stream Client - вертає потік твітів 2. Але у нас є проблема - мало твітів! 3. Рішення просте
  • #23 Ось так
  • #24 Ось повне рішення! ПРАВДА БАГАТО - 17 рядків коду? Порівняємо з діаграмою - має бути щось схоже!
  • #25 Ось повне рішення! ПРАВДА БАГАТО - 17 рядків коду? Порівняємо з діаграмою - має бути щось схоже!
  • #26 ЩЕ РАЗ НАЗАД - ДЛЯ ПОРІВНЯННЯ ОК, підіб’ємо підсумки?
  • #28 RxJava - Compositional Event Systems (CES) Різниця у формальному визначенні, для нас це не важливо
  • #29 Думали усе? Я ж сказав що легко тестити! TestSubscriber - частина RxJava TestScheduler - для генерації певних подій СПОДІВАЮСЬ ВИ ПОБАЧИЛИ: RxJava це круто
  • #30 Ну от, така от RxJava за 20 хв. Готовий відповісти на питання, маємо 3-5 хв. 2-3 питання Останнє питання: у нас ще одна презентація
  • #31 Дякую за увагу Будуть патання - гукайте