SlideShare a Scribd company logo
To be reactive...
Joker<?>
RxJava
Senior developer
Backend & DevOps
2
Альфа-Лаборатория
Максим Гореликов
@gorelikoff
gorelikov.max@gmail.com
Краткое содержание
Содержание 3
● Предыстория
● Ищем варианты
Краткое содержание
Содержание 4
● Предыстория
● Ищем варианты
● Обзор RxJava
● Что там под капотом?
Краткое содержание
Содержание 5
● Предыстория
● Ищем варианты
● Обзор RxJava
● Что там под капотом?
● А дальше?
● Выводы
Знакомая архитектура?
Предыстория 6
Client DatabaseBackend
Наш вариант
Предыстория 7
Client 1
Database
Plain
Old
Backend
Client 2
Client N
...
Наш вариант
Предыстория 8
Client 1
Database
Plain
Old
Backend
Client 2
Client N
Заслуженный enterprise
...
Новый вариант
Предыстория 9
Client 1
Database
Plain
Old
Backend
Client 2
Client N
Middle
...
Новый вариант
Предыстория 10
Database
Plain
Old
Backend
Ограничения
Client 1
Client 2
Client N
Middle
...
Новый вариант
Предыстория 11
Database
Plain
Old
Backend
Контролируемая, но свобода!
Client 1
Client 2
Client N
Middle
...
Предыстория 12
Middle
Предыстория 13
Middle. Внимание, buzzwords!
Микросервисы DevOps
Предыстория 14
Middle. Внимание, buzzwords!
Spring cloud
Микросервисы DevOps
Предыстория 15
Middle. Внимание, buzzwords!
Spring cloud
Микросервисы DevOps
Предыстория 16
Наш middle
Gateway
MS1
MS2
MS3
MS4
Предыстория 17
Наш middle
Gateway
MS1
MS2
MS3
MS4
WS1WS2WS4WS5WS3
Предыстория 18
Наш middle
Gateway
MS1
MS2
MS3
MS4
WS1WS2WS4WS5WS3
100ms
200ms
300ms
100ms
Предыстория 19
Наш middle
Gateway
MS1
MS2
MS3
MS4
WS1WS2WS4WS5WS3
5000ms
100ms
200ms
300ms
100ms
Предыстория 20
Наш middle
Gateway
MS1
MS2
MS3
MS4
WS1WS2WS4WS5WS3
5000ms
100ms
200ms
300ms
100 - 6000ms
Архитектура
Предыстория 21
Client 1
Client 2
Client N
...
DatabaseMiddle
WS 1
WS 2
WS N
...
Архитектура. Паутинка-style.
Предыстория 22
Client 1
Client 2
Client N
...
Database
WS 1
WS 2
WS N
...
Предыстория 23
Условия
● Middle занимается сбором данных из различных источников
● Время ответа различных источников может сильно
различаться и нам неподконтрольно
Предыстория 24
Желания
● Хотим удобную, параллельную загрузку данных с
возможностью обработки по мере их готовности
● Хотим сами отдавать данные по мере их готовности
Предыстория 25
Желания
● Хотим удобную, параллельную загрузку данных с
возможностью обработки по мере их готовности
● Хотим сами отдавать данные по мере их готовности
Ищем варианты 26
Базовые стратегии
● Pull-based стратегия
● Push-based стратегия
Ищем варианты 27
Pull-based стратегия
Consumer Func
Execute
Wait for Result
Ищем варианты 28
Pull-based стратегия
● Подходит для случаев, когда данные готовы, например,
параллельные вычисления
Ищем варианты 29
Pull-based стратегия
● Подходит для случаев, когда данные готовы, например,
параллельные вычисления
● На ней основаны j.u.Stream и Fork/Join-фреймворки
Ищем варианты 30
Pull-based стратегия
● Подходит для случаев, когда данные готовы, например,
параллельные вычисления
● На ней основаны j.u.Stream и Fork/Join-фреймворки
● Плохо подходит для случаев с внешними блокирующими
вызовами
Ищем варианты 31
ForkJoin
Ищем варианты 32
Pull-based. Посмотреть.
Fork/Join: особенности реализации... (А. Шипилёв и С. Куксенко)
Ищем варианты 33
Push-based стратегия
Producer Subscriber
Subscribe
Notify
Ищем варианты 34
Push-based стратегия
● Подходит для случаев с блокирующими вызовами
Ищем варианты 35
Push-based стратегия
● Подходит для случаев с блокирующими вызовами
● Сложнее модель исполнения в многопоточности
● Сложнее контролировать ресурсы для исполнения
Ищем варианты 36
Push-based стратегия
● Подходит для случаев с блокирующими вызовами
● Сложнее модель исполнения в многопоточности
● Сложнее контролировать ресурсы для исполнения
● На ней основан CompletableFuture
Ищем варианты 37
Push-based. Посмотреть
CompletableFuture in Java 8, asynchronous processing done right
(Tomasz Nurkiewicz)
Ищем варианты 38
А можно и то и другое?
The masochist-only CountedCompleter class requires manual
continuation passing transforms of fork/join style processing that can
co-exist with reactive processing.
Doug Lea
Ищем варианты 39
А если очень хочется?
And there exists a hybrid solution that we use in "Reactive Streams",
which at runtime dynamically switches between push and pull
depending on if the consumer can't keep up or the producer can't keep
up, without having to block or do any load-shedding.
Viktor Klang
Ищем варианты 40
Reactive Streams
Инициатива в попытке создать стандарт для асинхронной
обработки данных с возможностью неблокирующего контроля над
нагрузкой (backpressure).
Ищем варианты 41
Reactive Streams. Реализации
● Akka streams
● Project reactor
● ReactiveX (RxJava, RxScala, RxJs, etc.)
Ищем варианты 42
Смотрим бенчмарки?
Ищем варианты 43
А если честно...
● Модель RxJava выглядит проще и привычнее акторов
Ищем варианты 44
А если честно...
● Модель RxJava выглядит проще и привычнее акторов
● Reactor выглядит скорее как основа для фреймворков
Ищем варианты 45
А если честно...
● Модель RxJava выглядит проще и привычнее акторов
● Reactor выглядит скорее как основа для фреймворков
● RxJava - одна библиотека весом в 1Mb без доп. зависимостей
Ищем варианты 46
А если честно...
● Модель RxJava выглядит проще и привычнее акторов
● Reactor выглядит скорее как основа для фреймворков
● RxJava - одна библиотека весом в 1Mb без доп. зависимостей
● Документация по ReactiveX
Обзор RxJava 47
RxJava
Библиотека, основанная на шаблоне Observer и построенная по
правилам reactive streams
Обзор RxJava 48
Rx. История
● Rx.NET 17.11.2009
● RxJS 17.03.2010
● RxJava 1.0 (by Netflix) xx.11.2012
● RxJava 2.0 ??.??.????
Обзор RxJava 49
Rx. Полиглот
● RxScala
● RxClojure
● RxGroovy
● RxJRuby
● Rx.ruby
● RxCpp
● RxPY
● RxKotlin
● RxSwift
● RxPHP
● RxNetty
● RxAndroid
● RxCocoa
Обзор RxJava 50
RxJava. Базовые элементы
Observable Observer
Subscribe/control
Notify
Обзор RxJava 51
Observer
● onNext
● onError
● onCompleted
Обзор RxJava 52
Observable
● subscribe
● Еще ~300 методов-операторов
Обзор RxJava 53
ReactiveX Marbles
skip(10)
take(5)
0 1 ... 10 11 12 13 14 15 16 17 18
10 11 12 13 14 15 16 17 18
10 11 12 13 14
Обзор RxJava 54
Оператор Map
Обзор RxJava 55
Оператор FlatMap
Обзор RxJava 56
Оператор Zip
Обзор RxJava 57
Пример цепочки
Observable.zip(requestCards(userId),requestAccounts(userId),
(cards, accounts) -> collate(cards, accounts))
Обзор RxJava 58
Пример цепочки
Observable.zip(requestCards(userId),requestAccounts(userId),
(cards, accounts) -> collate(cards, accounts))
.flatMap(sources -> loadPreferences(sources))
Обзор RxJava 59
Пример цепочки
Observable.zip(requestCards(userId),requestAccounts(userId),
(cards, accounts) -> collate(cards, accounts))
.flatMap(sources -> loadPreferences(sources))
.map(preferedSources -> range(preferedSources))
Обзор RxJava 60
Пример цепочки
Observable.zip(requestCards(userId),requestAccounts(userId),
(cards, accounts) -> collate(cards, accounts))
.flatMap(sources -> loadPreferences(sources))
.map(preferedSources -> range(preferedSources))
.onErrorResumeNext(err -> {
log.error("Some error", err);
return loadFromCache(user);
})
Обзор RxJava 61
Пример цепочки
Observable.zip(requestCards(userId),requestAccounts(userId),
(cards, accounts) -> collate(cards, accounts))
.flatMap(sources -> loadPreferences(sources))
.map(preferedSources -> range(preferedSources))
.onErrorResumeNext(err -> {
log.error("Some error", err);
return loadFromCache(user);
})
.subscribe(result -> render(result));
Обзор RxJava 62
Сюрприз
По умолчанию observable-цепочка исполняется в текущем потоке.
Обзор RxJava 63
Сюрприз
По умолчанию observable-цепочка исполняется в текущем потоке.
Да-да и даже zip!
Обзор RxJava 64
Как это работает
zip SubscriberflatMap
onXXX
subscribe
onXXX
Обзор RxJava 65
Как это работает
zip Subscriber
onXXX
flatMap
onStart
onXXX
Обзор RxJava 66
Как это работает
zip Subscriber
onStart
onXXX
flatMap
onStart
onXXX
Обзор RxJava 67
Как это работает
zip Subscriber
onStart
onXXX
flatMap
onStart
onXXX
Обзор RxJava 68
Как это работает
zip Subscriber
onStart
onXXX
flatMap
onStart
onXXX
Обзор RxJava 69
Как это работает
zip Subscriber
onStart
onXXX
flatMap
onStart
onXXX
Обзор RxJava 70
Работает на текущем потоке
zip Subscriber
onXXX
flatMap
onXXX
Current Thread
onStart onStart
Обзор RxJava 71
Маgic is impossible
without special equipment
Обзор RxJava 72
Подключаем потоки
● subscribeOn
● observeOn
Обзор RxJava 73
Подключаем потоки
● subscribeOn
● observeOn
Обзор RxJava 74
SubscribeOn. Пример
Observable.range(1,1000)
.map(res -> res + 1)
.flatMap(res -> Observable.just(res))
.subscribeOn(Schedulers.computation())
.subscribe(result -> System.out.println(result))
Обзор RxJava 75
SubscribeOn. Изнутри
ExecutorService exec = Executors.newFixedThreadPool(2);
Subscriber<T> subscribeOn = o -> {
exec.submit(() -> sourceSubject.subscribe(o));
};
Обзор RxJava 76
SubscribeOn. Результат
range Subscribermap
Threads from Scheduler by subscribeOn
flatMap
Обзор RxJava 77
SubscribeOn. Дважды
Observable.range(1,1000)
.subscribeOn(Schedulers.io())
.map(res -> res + 1)
.flatMap(res -> Observable.just(res))
.subscribeOn(Schedulers.computation())
.subscribe(result -> System.out.println(result))
Обзор RxJava 78
SubscribeOn. Изнутри
ExecutorService exec = Executors.newFixedThreadPool(2);
ExecutorService exec2 = Executors.newFixedThreadPool(2);
Subscriber<T> subscribeOn = o -> {
exec2.submit(() ->
exec.submit(() -> sourceSubject.subscribe(o))
);
};
Обзор RxJava 79
SubscribeOn. Дважды
Observable.range(1,1000)
.subscribeOn(Schedulers.io())
.map(res -> res + 1)
.flatMap(res -> Observable.just(res))
.subscribeOn(Schedulers.computation())
.subscribe(result -> System.out.println(result))
Обзор RxJava 80
SubscribeOn. Рекомендации
Не стоит добавлять subscribeOn внутри своих библиотек, так как
пользователи не смогут переопределить заданное поведение.
Обзор RxJava 81
Подключаем потоки
● subscribeOn
● observeOn
Обзор RxJava 82
ObserveOn. Пример
Observable.range(1,1000)
.map(res -> res + 1)
.observeOn(Schedulers.io())
.flatMap(res -> Observable.just(res))
.subscribeOn(Schedulers.computation())
.subscribe(result -> System.out.println(result))
Обзор RxJava 83
ObserveOn. Изнутри
ExecutorService exec = Executors.newFixedThreadPool(2);
Subscriber<T> observeOn = o -> {
sourceSubject.subscribe(new Observer<T>() {
@Override
public void onNext(T t) {
exec.submit(() -> o.onNext(t));
}
...
});
};
Обзор RxJava 84
ObserveOn. Пример
Observable.range(1,1000)
.map(res -> res + 1)
.observeOn(Schedulers.io)
.flatMap(res -> Observable.just(res))
.subscribeOn(Schedulers.computation())
.subscribe(result -> System.out.println(result))
Обзор RxJava 85
ObserveOn. Результат
Threads by observeOn
range Subscribermap flatMap
Threads from Scheduler by subscribeOn
observeOn
Обзор RxJava 86
ObserveOn. Дважды
Observable.range(1,1000)
.map(res -> res + 1)
.observeOn(Schedulers.io())
.flatMap(res -> Observable.just(res))
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.newThread())
.subscribe(result -> System.out.println(result))
Обзор RxJava 87
ObserveOn. Разбиваем дальше
observeOn
range Subscribermap flatMap
Threads from Scheduler by subscribeOn
observeOn
Обзор RxJava 88
Роль методов
● subscribeOn - отвечает за pool, на котором исполняется
Producer
Обзор RxJava 89
Роль методов
● subscribeOn - отвечает за pool, на котором исполняется
Producer
● observeOn - отвечает за pool, на котором исполняются
операторы и подписчик
Обзор RxJava 90
Ситуация
observeOn
range Subscribermap flatMap
Threads from Scheduler by subscribeOn
observeOn
Обзор RxJava 91
Подписчик может не успевать
observeOn
range Subscribermap flatMap
observeOn
Обзор RxJava 92
Backpressure
Механизм для балансировки нагрузки без блокировок и сброса
событий
Обзор RxJava 93
Backpressure. Пример
MQ BankBrains
Обзор RxJava 94
Backpressure. Пример
MQ BankBrains
Обзор RxJava 95
Backpressure. Пример
MQ BankBrains
Обзор RxJava 96
Backpressure. Пример
MQ BankBrains
Обзор RxJava 97
Backpressure. Пример
MQ BankBrains
1000 rps
1000 rps10 000 rps10 000 rps
Обзор RxJava 98
Нет времени разбираться!
Обзор RxJava 99
Есть варианты
● Sample (Throttling)
● Buffer
● Window
● ...
Обзор RxJava 100
Sample(Throttling)
Обзор RxJava 101
Buffer
Обзор RxJava 102
Backpressure. Пример
MQ BankBrains
1000 rps
1000 rps10 000 rps10 000 rps
Обзор RxJava 103
sourceSubject.subscribe(new Subscriber() {
@Override
public void onNext(Object o) {
//process signal
request(10);
}
});
Backpressure. Pull-based
Обзор RxJava 104
class Subscriber() {
@Override
public void onStart(Object o) {
request(Long.MAX_VALUE);
}
});
Subscribe. Push-based
Обзор RxJava 105
Backpressure. Пример
MQ BankBrains
1000 rps
1000 rps10 000 rps10 000 rps
Backpressure
Обзор RxJava 106
Backpressure. Пример
MQ BankBrains
1000 rps
1000 rps1000 rps1000 rps
Backpressure
Что там под капотом 107
Основы изучены
● Написание простейших цепочек
● Распределение работы по потокам
● Балансировка нагрузки через backpressure
Что там под капотом 108
Немного углубимся
В большинстве случаев вам хватит стандартного набора
операторов, но рано или поздно захочется расширить
функциональность.
Что там под капотом 109
Observable.Operator
Observable.Operator<Integer, Integer> operator = subscriber -> {
new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
//do some work
}
};
Что там под капотом 110
Пишем свой оператор
Observable.Operator<Integer, Integer> filter100 = subscriber -> {
return new Subscriber<Integer>() {
public void onNext(Integer el) {
if(el < 100)
subscriber.onNext(el);
else
onCompleted();
}
…
};
Что там под капотом 111
Встраиваем оператор
Observable.range(1,1000)
.lift(filter100)
.observeOn(Schedulers.newThread())
.flatMap(res -> someBlockingCall())
.subscribe(result -> System.out.println(result));
Что там под капотом 112
Работает!
Еще и многопоточно!
Что там под капотом 113
Немного подправим оператор
Observable.range(1,1000)
.lift(filter200)
.observeOn(Schedulers.newThread())
.flatMap(res -> someBlockingCall(res))
.subscribe(res -> System.out.println(res));
Что там под капотом 114
Упс!
rx.exceptions.MissingBackpressureException
Что там под капотом 115
Ищем плавающие баги
Что там под капотом 116
Разрыв обратной связи
range flatMap
onXXX
filter200
onXXX
Что там под капотом 117
Исправляем оператор
Observable.Operator<Integer, Integer> filter100 = subscriber -> {
return new Subscriber<Integer>(subscriber) {
public void onNext(Integer el) {
if(el < 200)
subscriber.onNext(el);
else
onCompleted();
}
…
};
Что там под капотом 118
А почему работало?
Внутри обертки OperatorObserveOn есть Queue размером в 128
элементов, потому что backpressure был не всегда.
Что там под капотом 119
А почему 128?
Читайте benchmark в комментариях к очереди в observeOn :)
Что там под капотом 120
Оператор пропуска нечетных
Observable.Operator<Integer, Integer> skipOdd = subs -> {
return new Subscriber<Integer>(subs) {
public void onNext(Integer i) {
if (i % 2 == 0)
subs.onNext(i);
}
...
};
Что там под капотом 121
Подключаем оператор
Observable.Operator<Integer, Integer> skipOdd = subs -> {
return new Subscriber<Integer>(subs) {
public void onNext(Integer i) {
if (i % 2 == 0)
subs.onNext(i);
}
...
};
Observable.range(1, 100)
.lift(skipOdd)
.take(10)
.subscribe(res -> System.out.println(res));
Что там под капотом 122
Вывод. Чего-то не хватает
2
4
6
8
10
Что там под капотом 123
Вспоминаем про backpressure
Observable.Operator<Integer, Integer> skipOdd = subs -> {
return new Subscriber<Integer>(subs) {
public void onNext(Integer i) {
if (i % 2 == 0)
subs.onNext(i);
else
request(1);
}
...
};
Что там под капотом 124
Продолжаем писать оператор
Operator<Integer, Integer> filter200 = subscriber -> {
return new Subscriber<Integer>() {
public void onNext(Integer el) {
if(el < 200)
subscriber.onNext(el);
else
onCompleted();
}
public void onCompleted() {
subscriber.onCompleted();
unsubscribe();
}
…
};
Что там под капотом 125
Есть потоки, есть проблемы
Operator<Integer, Integer> filter200 = subscriber -> {
return new Subscriber<Integer>() {
public void onNext(Integer el) {
if(el < 200)
subscriber.onNext(el);
else
onCompleted();
}
public void onCompleted() {
subscriber.onCompleted();
unsubscribe();
}
…
};
Thread 1
Что там под капотом 126
Есть потоки, есть проблемы
Operator<Integer, Integer> filter200 = subscriber -> {
return new Subscriber<Integer>() {
public void onNext(Integer el) {
if(el < 200)
subscriber.onNext(el);
else
onCompleted();
}
public void onCompleted() {
subscriber.onCompleted();
unsubscribe();
}
…
};
Thread 1
Thread 2
Что там под капотом 127
Гарантии #1-2
● События onNext, onError и onComplete должны быть доставлены в
Subscriber последовательно
Что там под капотом 128
Гарантии #1-2
● События onNext, onError и onComplete должны быть доставлены в
Subscriber последовательно
● События onError и onCompleted терминальны
Что там под капотом 129
Добавляем сериализацию
Operator<Integer, Integer> filter200 = c -> {
Subscriber<Integer> subscriber = new SerializedSubscriber<>(c);
return new Subscriber<Integer>() {
public void onNext(Integer el) {
if(el < 100)
subscriber.onNext(el);
else
onCompleted();
}
public void onCompleted() {
subscriber.onCompleted();
unsubscribe();
}
…
};
Что там под капотом 130
Есть потоки, нет проблем
Observable.range(1,1000)
.lift(filter200)
.flatMap(sources -> loadPreferences(sources))
.observeOn(Schedulers.io())
.subscribe(res -> System.out.println(res))
Что там под капотом 131
Serialization
if (emitting) {
global = value;
return;
}
emitting = true;
synchronized(this)
Value
Что там под капотом 132
Serialization
if (emitting) {
global = value;
return;
}
emitting = true;
if (global == null) {
emitting = false;
return;
}
result = global;
global = null;
synchronized(this) synchronized(this)
Value
Что там под капотом 133
Serialization
if (emitting) {
global = value;
return;
}
emitting = true;
T result;
consumer.accept(result)
if (global == null) {
emitting = false;
return;
}
result = global;
global = null;
synchronized(this) synchronized(this)
Value
for (;;)
Value
Что там под капотом 134
Serialization
if (emitting) {
??? q = queue;
if (q == null) {
q = new ???();
queue = q;
}
q.add(event);
return;
}
emitting = true;
synchronized(this)
Что там под капотом 135
Serialization
q = queue;
if (q == null) {
emitting = false;
return;
}
queue = null;
synchronized(this)if (emitting) {
??? q = queue;
if (q == null) {
q = new ???();
queue = q;
}
q.add(event);
return;
}
emitting = true;
synchronized(this)
Что там под капотом 136
Serialization
??? q;
q.forEach(consumer);
q = queue;
if (q == null) {
emitting = false;
return;
}
queue = null;
synchronized(this)if (emitting) {
??? q = queue;
if (q == null) {
q = new ???();
queue = q;
}
q.add(event);
return;
}
emitting = true;
synchronized(this)
for (;;)
Что там под капотом 137
Serialization
List q;
q.forEach(consumer);
q = queue;
if (q == null) {
emitting = false;
return;
}
queue = null;
synchronized(this)if (emitting) {
List q = queue;
if (q == null) {
q = new ArrayList();
queue = q;
}
q.add(event);
return;
}
emitting = true;
synchronized(this)
for (;;)
Что там под капотом 138
Serialization. Emitter-loop
Что там под капотом 139
Serialization. Emitter-loop
− Блокирующий
Что там под капотом 140
Serialization. Emitter-loop
− Блокирующий
+ Могут происходить оптимизации Biased locking и Lock elision
Что там под капотом 141
Serialization. Emitter-loop
− Блокирующий
+ Могут происходить оптимизации Biased locking и Lock elision
● Работает внутри RxJava version 1.X
Что там под капотом 142
Serialization. Queue-drain
final Queue<T> queue = new ????Queue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value)); // (1)
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 143
Serialization. Queue-drain
final Queue<T> queue = new ????Queue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value));
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 144
Serialization. Queue-drain
final Queue<T> queue = new ????Queue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value));
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 145
Serialization. Queue-drain
final Queue<T> queue = new ????Queue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value));
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 146
Serialization. Queue-drain
final Queue<T> queue = new ????Queue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value));
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 147
Serialization. Queue-drain
final Queue<T> queue = new ????Queue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value));
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 148
Serialization. Queue-drain
final Queue<T> queue = new MpscLinkedQueue<>();
final AtomicInteger wip = new AtomicInteger();
public void drain(T value) {
queue.offer(Objects.requireNonNull(value));
if (wip.getAndIncrement() == 0) {
do {
wip.set(1);
T v;
while ((v = queue.poll()) != null) {
consumer.accept(v);
}
} while (wip.decrementAndGet() != 0);
}
}
Что там под капотом 149
MpscLinkedQueue
● Multiple producer Single consumer очередь из пакета JCTools
Что там под капотом 150
MpscLinkedQueue
● Multiple producer Single consumer очередь из пакета JCTools
● Wait-free offer и Lock-free poll
Что там под капотом 151
MpscLinkedQueue. Почитать
Porting Pitfalls:
Turning D.Vyukov MPSC Wait-free queue into a j.u.Queue
Что там под капотом 152
Serialization. Queue-drain
+ Почти неблокирующий
Что там под капотом 153
Serialization. Queue-drain
+ Почти неблокирующий
● Работает внутри RxJava version 2.X
Выводы 154
Выдохнули :)
● Знаем, как выглядит Rx снаружи
● Понимаем, что внутри Rx
А дальше? 155
А как же передача данных?
А дальше? 156
ThreadPerRequest
Client
Client
Client
Handler
Thread
Handler
Thread
Handler
Thread
+
Rx chain
Rx chain
Rx chain
А дальше? 157
Клиент все равно ждет
Client
Client
Client
Handler
Thread
Handler
Thread
Handler
Thread
+
Rx chain
Rx chain
Rx chain
А дальше? 158
Клиент все равно ждет
Client
Client
Client
Handler
Thread
Handler
Thread
Handler
Thread
+
Rx chain
Rx chain
Rx chain
А дальше? 159
Канал данных не завершен
А дальше? 160
Дальнейшие шаги
● Chunked Transfer Encoding для передачи данных по частям
А дальше? 161
Дальнейшие шаги
● Chunked Transfer Encoding для передачи данных по частям
● Non-blocking NIO и Async Servlet
Выводы 162
Выводы
● Базовый набор операторов RxJava прост и удобен для
построения цепочек сбора и обработки данных
Выводы 163
Выводы
● Базовый набор операторов RxJava прост и удобен для
построения цепочек сбора и обработки данных
● Писать свои операторы просто, но надо понимать механику,
ошибки могут запомниться вам надолго
Выводы 164
Выводы
● Базовый набор операторов RxJava прост и удобен для
построения цепочек сбора и обработки данных
● Писать свои операторы просто, но надо понимать механику,
ошибки могут запомниться вам надолго
● Rx-цепочки внутри синхронного приложения не являются
полноценным решением
Почитать / Посмотреть 165
Почитать / Посмотреть
● http://www.reactivemanifesto.org/ - прочитать и подписать
● https://github.com/reactive-streams/reactive-streams-jvm/
● http://reactivex.io/ - документация ReactiveX
● https://github.com/ReactiveX/RxJava/wiki - wiki проекта
● http://akarnokd.blogspot.ru/ - блог о внутренностях RxJava
● http://tomstechnicalblog.blogspot.ru/ - понятные объяснения
Вопросы
Вопросы?

More Related Content

What's hot

PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
pgdayrussia
 
Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)
Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)
Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)
Ontico
 
Философия Application Security
Философия Application SecurityФилософия Application Security
Философия Application Security
Vladimir Kochetkov
 
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ontico
 
Система обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyСистема обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyRegn
 
Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++
Yauheni Akhotnikau
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
Vasil Remeniuk
 
Поддержка Java 8 в Excelsior JET
Поддержка Java 8 в Excelsior JET Поддержка Java 8 в Excelsior JET
Поддержка Java 8 в Excelsior JET
Nikita Lipsky
 
Криптология в анализе защищённости
Криптология в анализе защищённостиКриптология в анализе защищённости
Криптология в анализе защищённости
beched
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)
Alexander Gornik
 
"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013
"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013
"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013Vladimir Ivanov
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгирование
Python Meetup
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and cats
corehard_by
 
JVM: краткий курс общей анатомии
JVM: краткий курс общей анатомииJVM: краткий курс общей анатомии
JVM: краткий курс общей анатомии
Nikita Lipsky
 
Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодня
Alexander Granin
 
ук 03.002.01 2011
ук 03.002.01 2011ук 03.002.01 2011
ук 03.002.01 2011etyumentcev
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.
Igor Shkulipa
 
C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.
Igor Shkulipa
 

What's hot (20)

PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
 
Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)
Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)
Консервативный Backend на Node.js / Дмитрий Ляпин (Recrumatic)
 
Философия Application Security
Философия Application SecurityФилософия Application Security
Философия Application Security
 
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
Ангелы и демоны многопоточного программирования / Алексей Федоров (Одноклассн...
 
Система обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyСистема обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на Groovy
 
Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 
Поддержка Java 8 в Excelsior JET
Поддержка Java 8 в Excelsior JET Поддержка Java 8 в Excelsior JET
Поддержка Java 8 в Excelsior JET
 
Криптология в анализе защищённости
Криптология в анализе защищённостиКриптология в анализе защищённости
Криптология в анализе защищённости
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)
 
Bytecode
BytecodeBytecode
Bytecode
 
"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013
"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013
"Invokedynamic: роскошь или необходимость?"@ JavaOne Moscow 2013
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгирование
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and cats
 
JVM: краткий курс общей анатомии
JVM: краткий курс общей анатомииJVM: краткий курс общей анатомии
JVM: краткий курс общей анатомии
 
Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодня
 
ук 03.002.01 2011
ук 03.002.01 2011ук 03.002.01 2011
ук 03.002.01 2011
 
JRebel
JRebelJRebel
JRebel
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.
 
C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.
 

Similar to To be reactive...RxJava

Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"
Fwdays
 
Будни тестирования Cassandr-ы
Будни тестирования Cassandr-ыБудни тестирования Cassandr-ы
Будни тестирования Cassandr-ы
SQALab
 
Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)
Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)
Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)
Ontico
 
Организация процесса регулярной обработки больших объемов данных
Организация процесса регулярной обработки больших объемов данныхОрганизация процесса регулярной обработки больших объемов данных
Организация процесса регулярной обработки больших объемов данных
CodeFest
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
View как чистая функция от состояния базы данных  - Илья Беда, bro.agencyView как чистая функция от состояния базы данных  - Илья Беда, bro.agency
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
it-people
 
Иван Карев — Клиентская оптимизация
Иван Карев — Клиентская оптимизацияИван Карев — Клиентская оптимизация
Иван Карев — Клиентская оптимизацияYandex
 
Быстрое построение backendов c помощью реактивных потоков
Быстрое построение backendов c помощью реактивных потоковБыстрое построение backendов c помощью реактивных потоков
Быстрое построение backendов c помощью реактивных потоков
CodeFest
 
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...CodeFest
 
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
it-people
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
Denis Tsvettsih
 
Промышленный подход к тюнингу PostgreSQL: эксперименты над базами данных
Промышленный подход к тюнингу PostgreSQL: эксперименты над базами данныхПромышленный подход к тюнингу PostgreSQL: эксперименты над базами данных
Промышленный подход к тюнингу PostgreSQL: эксперименты над базами данных
Nikolay Samokhvalov
 
Java 9: what is there beyond modularization
Java 9: what is there beyond modularizationJava 9: what is there beyond modularization
Java 9: what is there beyond modularization
Ivan Krylov
 
Java Ahead-Of-Time compilation
Java Ahead-Of-Time compilationJava Ahead-Of-Time compilation
Java Ahead-Of-Time compilation
Nikita Lipsky
 
Масштабируемая архитектура фронтенда
Масштабируемая архитектура фронтендаМасштабируемая архитектура фронтенда
Масштабируемая архитектура фронтенда
Roman Dvornov
 
Scala performance под капотом
Scala performance под капотомScala performance под капотом
Scala performance под капотом
Roman Grebennikov
 
мониторинг производительности Web приложений на python
мониторинг производительности Web приложений на pythonмониторинг производительности Web приложений на python
мониторинг производительности Web приложений на python
Slach
 
Andrey Borodin "Architecture of online backup for various DBMS"
Andrey Borodin "Architecture of online backup for various DBMS"Andrey Borodin "Architecture of online backup for various DBMS"
Andrey Borodin "Architecture of online backup for various DBMS"
Fwdays
 
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
Кирилл Толкачёв
 
Мониторинг и отладка MySQL: максимум информации при минимальных потерях
Мониторинг и отладка MySQL: максимум информации при минимальных потеряхМониторинг и отладка MySQL: максимум информации при минимальных потерях
Мониторинг и отладка MySQL: максимум информации при минимальных потерях
Sveta Smirnova
 

Similar to To be reactive...RxJava (20)

Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"
 
Будни тестирования Cassandr-ы
Будни тестирования Cassandr-ыБудни тестирования Cassandr-ы
Будни тестирования Cassandr-ы
 
Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)
Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)
Как мы разрабатываем новый фронтенд / Филипп Нехаев (Tinkoff.ru)
 
Организация процесса регулярной обработки больших объемов данных
Организация процесса регулярной обработки больших объемов данныхОрганизация процесса регулярной обработки больших объемов данных
Организация процесса регулярной обработки больших объемов данных
 
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
View как чистая функция от состояния базы данных  - Илья Беда, bro.agencyView как чистая функция от состояния базы данных  - Илья Беда, bro.agency
View как чистая функция от состояния базы данных - Илья Беда, bro.agency
 
Иван Карев — Клиентская оптимизация
Иван Карев — Клиентская оптимизацияИван Карев — Клиентская оптимизация
Иван Карев — Клиентская оптимизация
 
Быстрое построение backendов c помощью реактивных потоков
Быстрое построение backendов c помощью реактивных потоковБыстрое построение backendов c помощью реактивных потоков
Быстрое построение backendов c помощью реактивных потоков
 
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
 
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
"Модель акторов и параллелизм с использованием Akka" Зубов Максим, Naumen
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
 
Промышленный подход к тюнингу PostgreSQL: эксперименты над базами данных
Промышленный подход к тюнингу PostgreSQL: эксперименты над базами данныхПромышленный подход к тюнингу PostgreSQL: эксперименты над базами данных
Промышленный подход к тюнингу PostgreSQL: эксперименты над базами данных
 
Java 9: what is there beyond modularization
Java 9: what is there beyond modularizationJava 9: what is there beyond modularization
Java 9: what is there beyond modularization
 
Java Ahead-Of-Time compilation
Java Ahead-Of-Time compilationJava Ahead-Of-Time compilation
Java Ahead-Of-Time compilation
 
Масштабируемая архитектура фронтенда
Масштабируемая архитектура фронтендаМасштабируемая архитектура фронтенда
Масштабируемая архитектура фронтенда
 
Scala performance под капотом
Scala performance под капотомScala performance под капотом
Scala performance под капотом
 
Uawebchallenge.yandex.tank
Uawebchallenge.yandex.tankUawebchallenge.yandex.tank
Uawebchallenge.yandex.tank
 
мониторинг производительности Web приложений на python
мониторинг производительности Web приложений на pythonмониторинг производительности Web приложений на python
мониторинг производительности Web приложений на python
 
Andrey Borodin "Architecture of online backup for various DBMS"
Andrey Borodin "Architecture of online backup for various DBMS"Andrey Borodin "Architecture of online backup for various DBMS"
Andrey Borodin "Architecture of online backup for various DBMS"
 
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
 
Мониторинг и отладка MySQL: максимум информации при минимальных потерях
Мониторинг и отладка MySQL: максимум информации при минимальных потеряхМониторинг и отладка MySQL: максимум информации при минимальных потерях
Мониторинг и отладка MySQL: максимум информации при минимальных потерях
 

To be reactive...RxJava

  • 2. Senior developer Backend & DevOps 2 Альфа-Лаборатория Максим Гореликов @gorelikoff gorelikov.max@gmail.com
  • 3. Краткое содержание Содержание 3 ● Предыстория ● Ищем варианты
  • 4. Краткое содержание Содержание 4 ● Предыстория ● Ищем варианты ● Обзор RxJava ● Что там под капотом?
  • 5. Краткое содержание Содержание 5 ● Предыстория ● Ищем варианты ● Обзор RxJava ● Что там под капотом? ● А дальше? ● Выводы
  • 7. Наш вариант Предыстория 7 Client 1 Database Plain Old Backend Client 2 Client N ...
  • 8. Наш вариант Предыстория 8 Client 1 Database Plain Old Backend Client 2 Client N Заслуженный enterprise ...
  • 9. Новый вариант Предыстория 9 Client 1 Database Plain Old Backend Client 2 Client N Middle ...
  • 13. Предыстория 13 Middle. Внимание, buzzwords! Микросервисы DevOps
  • 14. Предыстория 14 Middle. Внимание, buzzwords! Spring cloud Микросервисы DevOps
  • 15. Предыстория 15 Middle. Внимание, buzzwords! Spring cloud Микросервисы DevOps
  • 21. Архитектура Предыстория 21 Client 1 Client 2 Client N ... DatabaseMiddle WS 1 WS 2 WS N ...
  • 22. Архитектура. Паутинка-style. Предыстория 22 Client 1 Client 2 Client N ... Database WS 1 WS 2 WS N ...
  • 23. Предыстория 23 Условия ● Middle занимается сбором данных из различных источников ● Время ответа различных источников может сильно различаться и нам неподконтрольно
  • 24. Предыстория 24 Желания ● Хотим удобную, параллельную загрузку данных с возможностью обработки по мере их готовности ● Хотим сами отдавать данные по мере их готовности
  • 25. Предыстория 25 Желания ● Хотим удобную, параллельную загрузку данных с возможностью обработки по мере их готовности ● Хотим сами отдавать данные по мере их готовности
  • 26. Ищем варианты 26 Базовые стратегии ● Pull-based стратегия ● Push-based стратегия
  • 27. Ищем варианты 27 Pull-based стратегия Consumer Func Execute Wait for Result
  • 28. Ищем варианты 28 Pull-based стратегия ● Подходит для случаев, когда данные готовы, например, параллельные вычисления
  • 29. Ищем варианты 29 Pull-based стратегия ● Подходит для случаев, когда данные готовы, например, параллельные вычисления ● На ней основаны j.u.Stream и Fork/Join-фреймворки
  • 30. Ищем варианты 30 Pull-based стратегия ● Подходит для случаев, когда данные готовы, например, параллельные вычисления ● На ней основаны j.u.Stream и Fork/Join-фреймворки ● Плохо подходит для случаев с внешними блокирующими вызовами
  • 32. Ищем варианты 32 Pull-based. Посмотреть. Fork/Join: особенности реализации... (А. Шипилёв и С. Куксенко)
  • 33. Ищем варианты 33 Push-based стратегия Producer Subscriber Subscribe Notify
  • 34. Ищем варианты 34 Push-based стратегия ● Подходит для случаев с блокирующими вызовами
  • 35. Ищем варианты 35 Push-based стратегия ● Подходит для случаев с блокирующими вызовами ● Сложнее модель исполнения в многопоточности ● Сложнее контролировать ресурсы для исполнения
  • 36. Ищем варианты 36 Push-based стратегия ● Подходит для случаев с блокирующими вызовами ● Сложнее модель исполнения в многопоточности ● Сложнее контролировать ресурсы для исполнения ● На ней основан CompletableFuture
  • 37. Ищем варианты 37 Push-based. Посмотреть CompletableFuture in Java 8, asynchronous processing done right (Tomasz Nurkiewicz)
  • 38. Ищем варианты 38 А можно и то и другое? The masochist-only CountedCompleter class requires manual continuation passing transforms of fork/join style processing that can co-exist with reactive processing. Doug Lea
  • 39. Ищем варианты 39 А если очень хочется? And there exists a hybrid solution that we use in "Reactive Streams", which at runtime dynamically switches between push and pull depending on if the consumer can't keep up or the producer can't keep up, without having to block or do any load-shedding. Viktor Klang
  • 40. Ищем варианты 40 Reactive Streams Инициатива в попытке создать стандарт для асинхронной обработки данных с возможностью неблокирующего контроля над нагрузкой (backpressure).
  • 41. Ищем варианты 41 Reactive Streams. Реализации ● Akka streams ● Project reactor ● ReactiveX (RxJava, RxScala, RxJs, etc.)
  • 43. Ищем варианты 43 А если честно... ● Модель RxJava выглядит проще и привычнее акторов
  • 44. Ищем варианты 44 А если честно... ● Модель RxJava выглядит проще и привычнее акторов ● Reactor выглядит скорее как основа для фреймворков
  • 45. Ищем варианты 45 А если честно... ● Модель RxJava выглядит проще и привычнее акторов ● Reactor выглядит скорее как основа для фреймворков ● RxJava - одна библиотека весом в 1Mb без доп. зависимостей
  • 46. Ищем варианты 46 А если честно... ● Модель RxJava выглядит проще и привычнее акторов ● Reactor выглядит скорее как основа для фреймворков ● RxJava - одна библиотека весом в 1Mb без доп. зависимостей ● Документация по ReactiveX
  • 47. Обзор RxJava 47 RxJava Библиотека, основанная на шаблоне Observer и построенная по правилам reactive streams
  • 48. Обзор RxJava 48 Rx. История ● Rx.NET 17.11.2009 ● RxJS 17.03.2010 ● RxJava 1.0 (by Netflix) xx.11.2012 ● RxJava 2.0 ??.??.????
  • 49. Обзор RxJava 49 Rx. Полиглот ● RxScala ● RxClojure ● RxGroovy ● RxJRuby ● Rx.ruby ● RxCpp ● RxPY ● RxKotlin ● RxSwift ● RxPHP ● RxNetty ● RxAndroid ● RxCocoa
  • 50. Обзор RxJava 50 RxJava. Базовые элементы Observable Observer Subscribe/control Notify
  • 51. Обзор RxJava 51 Observer ● onNext ● onError ● onCompleted
  • 52. Обзор RxJava 52 Observable ● subscribe ● Еще ~300 методов-операторов
  • 53. Обзор RxJava 53 ReactiveX Marbles skip(10) take(5) 0 1 ... 10 11 12 13 14 15 16 17 18 10 11 12 13 14 15 16 17 18 10 11 12 13 14
  • 57. Обзор RxJava 57 Пример цепочки Observable.zip(requestCards(userId),requestAccounts(userId), (cards, accounts) -> collate(cards, accounts))
  • 58. Обзор RxJava 58 Пример цепочки Observable.zip(requestCards(userId),requestAccounts(userId), (cards, accounts) -> collate(cards, accounts)) .flatMap(sources -> loadPreferences(sources))
  • 59. Обзор RxJava 59 Пример цепочки Observable.zip(requestCards(userId),requestAccounts(userId), (cards, accounts) -> collate(cards, accounts)) .flatMap(sources -> loadPreferences(sources)) .map(preferedSources -> range(preferedSources))
  • 60. Обзор RxJava 60 Пример цепочки Observable.zip(requestCards(userId),requestAccounts(userId), (cards, accounts) -> collate(cards, accounts)) .flatMap(sources -> loadPreferences(sources)) .map(preferedSources -> range(preferedSources)) .onErrorResumeNext(err -> { log.error("Some error", err); return loadFromCache(user); })
  • 61. Обзор RxJava 61 Пример цепочки Observable.zip(requestCards(userId),requestAccounts(userId), (cards, accounts) -> collate(cards, accounts)) .flatMap(sources -> loadPreferences(sources)) .map(preferedSources -> range(preferedSources)) .onErrorResumeNext(err -> { log.error("Some error", err); return loadFromCache(user); }) .subscribe(result -> render(result));
  • 62. Обзор RxJava 62 Сюрприз По умолчанию observable-цепочка исполняется в текущем потоке.
  • 63. Обзор RxJava 63 Сюрприз По умолчанию observable-цепочка исполняется в текущем потоке. Да-да и даже zip!
  • 64. Обзор RxJava 64 Как это работает zip SubscriberflatMap onXXX subscribe onXXX
  • 65. Обзор RxJava 65 Как это работает zip Subscriber onXXX flatMap onStart onXXX
  • 66. Обзор RxJava 66 Как это работает zip Subscriber onStart onXXX flatMap onStart onXXX
  • 67. Обзор RxJava 67 Как это работает zip Subscriber onStart onXXX flatMap onStart onXXX
  • 68. Обзор RxJava 68 Как это работает zip Subscriber onStart onXXX flatMap onStart onXXX
  • 69. Обзор RxJava 69 Как это работает zip Subscriber onStart onXXX flatMap onStart onXXX
  • 70. Обзор RxJava 70 Работает на текущем потоке zip Subscriber onXXX flatMap onXXX Current Thread onStart onStart
  • 71. Обзор RxJava 71 Маgic is impossible without special equipment
  • 72. Обзор RxJava 72 Подключаем потоки ● subscribeOn ● observeOn
  • 73. Обзор RxJava 73 Подключаем потоки ● subscribeOn ● observeOn
  • 74. Обзор RxJava 74 SubscribeOn. Пример Observable.range(1,1000) .map(res -> res + 1) .flatMap(res -> Observable.just(res)) .subscribeOn(Schedulers.computation()) .subscribe(result -> System.out.println(result))
  • 75. Обзор RxJava 75 SubscribeOn. Изнутри ExecutorService exec = Executors.newFixedThreadPool(2); Subscriber<T> subscribeOn = o -> { exec.submit(() -> sourceSubject.subscribe(o)); };
  • 76. Обзор RxJava 76 SubscribeOn. Результат range Subscribermap Threads from Scheduler by subscribeOn flatMap
  • 77. Обзор RxJava 77 SubscribeOn. Дважды Observable.range(1,1000) .subscribeOn(Schedulers.io()) .map(res -> res + 1) .flatMap(res -> Observable.just(res)) .subscribeOn(Schedulers.computation()) .subscribe(result -> System.out.println(result))
  • 78. Обзор RxJava 78 SubscribeOn. Изнутри ExecutorService exec = Executors.newFixedThreadPool(2); ExecutorService exec2 = Executors.newFixedThreadPool(2); Subscriber<T> subscribeOn = o -> { exec2.submit(() -> exec.submit(() -> sourceSubject.subscribe(o)) ); };
  • 79. Обзор RxJava 79 SubscribeOn. Дважды Observable.range(1,1000) .subscribeOn(Schedulers.io()) .map(res -> res + 1) .flatMap(res -> Observable.just(res)) .subscribeOn(Schedulers.computation()) .subscribe(result -> System.out.println(result))
  • 80. Обзор RxJava 80 SubscribeOn. Рекомендации Не стоит добавлять subscribeOn внутри своих библиотек, так как пользователи не смогут переопределить заданное поведение.
  • 81. Обзор RxJava 81 Подключаем потоки ● subscribeOn ● observeOn
  • 82. Обзор RxJava 82 ObserveOn. Пример Observable.range(1,1000) .map(res -> res + 1) .observeOn(Schedulers.io()) .flatMap(res -> Observable.just(res)) .subscribeOn(Schedulers.computation()) .subscribe(result -> System.out.println(result))
  • 83. Обзор RxJava 83 ObserveOn. Изнутри ExecutorService exec = Executors.newFixedThreadPool(2); Subscriber<T> observeOn = o -> { sourceSubject.subscribe(new Observer<T>() { @Override public void onNext(T t) { exec.submit(() -> o.onNext(t)); } ... }); };
  • 84. Обзор RxJava 84 ObserveOn. Пример Observable.range(1,1000) .map(res -> res + 1) .observeOn(Schedulers.io) .flatMap(res -> Observable.just(res)) .subscribeOn(Schedulers.computation()) .subscribe(result -> System.out.println(result))
  • 85. Обзор RxJava 85 ObserveOn. Результат Threads by observeOn range Subscribermap flatMap Threads from Scheduler by subscribeOn observeOn
  • 86. Обзор RxJava 86 ObserveOn. Дважды Observable.range(1,1000) .map(res -> res + 1) .observeOn(Schedulers.io()) .flatMap(res -> Observable.just(res)) .subscribeOn(Schedulers.computation()) .observeOn(Schedulers.newThread()) .subscribe(result -> System.out.println(result))
  • 87. Обзор RxJava 87 ObserveOn. Разбиваем дальше observeOn range Subscribermap flatMap Threads from Scheduler by subscribeOn observeOn
  • 88. Обзор RxJava 88 Роль методов ● subscribeOn - отвечает за pool, на котором исполняется Producer
  • 89. Обзор RxJava 89 Роль методов ● subscribeOn - отвечает за pool, на котором исполняется Producer ● observeOn - отвечает за pool, на котором исполняются операторы и подписчик
  • 90. Обзор RxJava 90 Ситуация observeOn range Subscribermap flatMap Threads from Scheduler by subscribeOn observeOn
  • 91. Обзор RxJava 91 Подписчик может не успевать observeOn range Subscribermap flatMap observeOn
  • 92. Обзор RxJava 92 Backpressure Механизм для балансировки нагрузки без блокировок и сброса событий
  • 93. Обзор RxJava 93 Backpressure. Пример MQ BankBrains
  • 94. Обзор RxJava 94 Backpressure. Пример MQ BankBrains
  • 95. Обзор RxJava 95 Backpressure. Пример MQ BankBrains
  • 96. Обзор RxJava 96 Backpressure. Пример MQ BankBrains
  • 97. Обзор RxJava 97 Backpressure. Пример MQ BankBrains 1000 rps 1000 rps10 000 rps10 000 rps
  • 98. Обзор RxJava 98 Нет времени разбираться!
  • 99. Обзор RxJava 99 Есть варианты ● Sample (Throttling) ● Buffer ● Window ● ...
  • 102. Обзор RxJava 102 Backpressure. Пример MQ BankBrains 1000 rps 1000 rps10 000 rps10 000 rps
  • 103. Обзор RxJava 103 sourceSubject.subscribe(new Subscriber() { @Override public void onNext(Object o) { //process signal request(10); } }); Backpressure. Pull-based
  • 104. Обзор RxJava 104 class Subscriber() { @Override public void onStart(Object o) { request(Long.MAX_VALUE); } }); Subscribe. Push-based
  • 105. Обзор RxJava 105 Backpressure. Пример MQ BankBrains 1000 rps 1000 rps10 000 rps10 000 rps Backpressure
  • 106. Обзор RxJava 106 Backpressure. Пример MQ BankBrains 1000 rps 1000 rps1000 rps1000 rps Backpressure
  • 107. Что там под капотом 107 Основы изучены ● Написание простейших цепочек ● Распределение работы по потокам ● Балансировка нагрузки через backpressure
  • 108. Что там под капотом 108 Немного углубимся В большинстве случаев вам хватит стандартного набора операторов, но рано или поздно захочется расширить функциональность.
  • 109. Что там под капотом 109 Observable.Operator Observable.Operator<Integer, Integer> operator = subscriber -> { new Subscriber<Integer>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Integer integer) { //do some work } };
  • 110. Что там под капотом 110 Пишем свой оператор Observable.Operator<Integer, Integer> filter100 = subscriber -> { return new Subscriber<Integer>() { public void onNext(Integer el) { if(el < 100) subscriber.onNext(el); else onCompleted(); } … };
  • 111. Что там под капотом 111 Встраиваем оператор Observable.range(1,1000) .lift(filter100) .observeOn(Schedulers.newThread()) .flatMap(res -> someBlockingCall()) .subscribe(result -> System.out.println(result));
  • 112. Что там под капотом 112 Работает! Еще и многопоточно!
  • 113. Что там под капотом 113 Немного подправим оператор Observable.range(1,1000) .lift(filter200) .observeOn(Schedulers.newThread()) .flatMap(res -> someBlockingCall(res)) .subscribe(res -> System.out.println(res));
  • 114. Что там под капотом 114 Упс! rx.exceptions.MissingBackpressureException
  • 115. Что там под капотом 115 Ищем плавающие баги
  • 116. Что там под капотом 116 Разрыв обратной связи range flatMap onXXX filter200 onXXX
  • 117. Что там под капотом 117 Исправляем оператор Observable.Operator<Integer, Integer> filter100 = subscriber -> { return new Subscriber<Integer>(subscriber) { public void onNext(Integer el) { if(el < 200) subscriber.onNext(el); else onCompleted(); } … };
  • 118. Что там под капотом 118 А почему работало? Внутри обертки OperatorObserveOn есть Queue размером в 128 элементов, потому что backpressure был не всегда.
  • 119. Что там под капотом 119 А почему 128? Читайте benchmark в комментариях к очереди в observeOn :)
  • 120. Что там под капотом 120 Оператор пропуска нечетных Observable.Operator<Integer, Integer> skipOdd = subs -> { return new Subscriber<Integer>(subs) { public void onNext(Integer i) { if (i % 2 == 0) subs.onNext(i); } ... };
  • 121. Что там под капотом 121 Подключаем оператор Observable.Operator<Integer, Integer> skipOdd = subs -> { return new Subscriber<Integer>(subs) { public void onNext(Integer i) { if (i % 2 == 0) subs.onNext(i); } ... }; Observable.range(1, 100) .lift(skipOdd) .take(10) .subscribe(res -> System.out.println(res));
  • 122. Что там под капотом 122 Вывод. Чего-то не хватает 2 4 6 8 10
  • 123. Что там под капотом 123 Вспоминаем про backpressure Observable.Operator<Integer, Integer> skipOdd = subs -> { return new Subscriber<Integer>(subs) { public void onNext(Integer i) { if (i % 2 == 0) subs.onNext(i); else request(1); } ... };
  • 124. Что там под капотом 124 Продолжаем писать оператор Operator<Integer, Integer> filter200 = subscriber -> { return new Subscriber<Integer>() { public void onNext(Integer el) { if(el < 200) subscriber.onNext(el); else onCompleted(); } public void onCompleted() { subscriber.onCompleted(); unsubscribe(); } … };
  • 125. Что там под капотом 125 Есть потоки, есть проблемы Operator<Integer, Integer> filter200 = subscriber -> { return new Subscriber<Integer>() { public void onNext(Integer el) { if(el < 200) subscriber.onNext(el); else onCompleted(); } public void onCompleted() { subscriber.onCompleted(); unsubscribe(); } … }; Thread 1
  • 126. Что там под капотом 126 Есть потоки, есть проблемы Operator<Integer, Integer> filter200 = subscriber -> { return new Subscriber<Integer>() { public void onNext(Integer el) { if(el < 200) subscriber.onNext(el); else onCompleted(); } public void onCompleted() { subscriber.onCompleted(); unsubscribe(); } … }; Thread 1 Thread 2
  • 127. Что там под капотом 127 Гарантии #1-2 ● События onNext, onError и onComplete должны быть доставлены в Subscriber последовательно
  • 128. Что там под капотом 128 Гарантии #1-2 ● События onNext, onError и onComplete должны быть доставлены в Subscriber последовательно ● События onError и onCompleted терминальны
  • 129. Что там под капотом 129 Добавляем сериализацию Operator<Integer, Integer> filter200 = c -> { Subscriber<Integer> subscriber = new SerializedSubscriber<>(c); return new Subscriber<Integer>() { public void onNext(Integer el) { if(el < 100) subscriber.onNext(el); else onCompleted(); } public void onCompleted() { subscriber.onCompleted(); unsubscribe(); } … };
  • 130. Что там под капотом 130 Есть потоки, нет проблем Observable.range(1,1000) .lift(filter200) .flatMap(sources -> loadPreferences(sources)) .observeOn(Schedulers.io()) .subscribe(res -> System.out.println(res))
  • 131. Что там под капотом 131 Serialization if (emitting) { global = value; return; } emitting = true; synchronized(this) Value
  • 132. Что там под капотом 132 Serialization if (emitting) { global = value; return; } emitting = true; if (global == null) { emitting = false; return; } result = global; global = null; synchronized(this) synchronized(this) Value
  • 133. Что там под капотом 133 Serialization if (emitting) { global = value; return; } emitting = true; T result; consumer.accept(result) if (global == null) { emitting = false; return; } result = global; global = null; synchronized(this) synchronized(this) Value for (;;) Value
  • 134. Что там под капотом 134 Serialization if (emitting) { ??? q = queue; if (q == null) { q = new ???(); queue = q; } q.add(event); return; } emitting = true; synchronized(this)
  • 135. Что там под капотом 135 Serialization q = queue; if (q == null) { emitting = false; return; } queue = null; synchronized(this)if (emitting) { ??? q = queue; if (q == null) { q = new ???(); queue = q; } q.add(event); return; } emitting = true; synchronized(this)
  • 136. Что там под капотом 136 Serialization ??? q; q.forEach(consumer); q = queue; if (q == null) { emitting = false; return; } queue = null; synchronized(this)if (emitting) { ??? q = queue; if (q == null) { q = new ???(); queue = q; } q.add(event); return; } emitting = true; synchronized(this) for (;;)
  • 137. Что там под капотом 137 Serialization List q; q.forEach(consumer); q = queue; if (q == null) { emitting = false; return; } queue = null; synchronized(this)if (emitting) { List q = queue; if (q == null) { q = new ArrayList(); queue = q; } q.add(event); return; } emitting = true; synchronized(this) for (;;)
  • 138. Что там под капотом 138 Serialization. Emitter-loop
  • 139. Что там под капотом 139 Serialization. Emitter-loop − Блокирующий
  • 140. Что там под капотом 140 Serialization. Emitter-loop − Блокирующий + Могут происходить оптимизации Biased locking и Lock elision
  • 141. Что там под капотом 141 Serialization. Emitter-loop − Блокирующий + Могут происходить оптимизации Biased locking и Lock elision ● Работает внутри RxJava version 1.X
  • 142. Что там под капотом 142 Serialization. Queue-drain final Queue<T> queue = new ????Queue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); // (1) if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 143. Что там под капотом 143 Serialization. Queue-drain final Queue<T> queue = new ????Queue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 144. Что там под капотом 144 Serialization. Queue-drain final Queue<T> queue = new ????Queue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 145. Что там под капотом 145 Serialization. Queue-drain final Queue<T> queue = new ????Queue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 146. Что там под капотом 146 Serialization. Queue-drain final Queue<T> queue = new ????Queue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 147. Что там под капотом 147 Serialization. Queue-drain final Queue<T> queue = new ????Queue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 148. Что там под капотом 148 Serialization. Queue-drain final Queue<T> queue = new MpscLinkedQueue<>(); final AtomicInteger wip = new AtomicInteger(); public void drain(T value) { queue.offer(Objects.requireNonNull(value)); if (wip.getAndIncrement() == 0) { do { wip.set(1); T v; while ((v = queue.poll()) != null) { consumer.accept(v); } } while (wip.decrementAndGet() != 0); } }
  • 149. Что там под капотом 149 MpscLinkedQueue ● Multiple producer Single consumer очередь из пакета JCTools
  • 150. Что там под капотом 150 MpscLinkedQueue ● Multiple producer Single consumer очередь из пакета JCTools ● Wait-free offer и Lock-free poll
  • 151. Что там под капотом 151 MpscLinkedQueue. Почитать Porting Pitfalls: Turning D.Vyukov MPSC Wait-free queue into a j.u.Queue
  • 152. Что там под капотом 152 Serialization. Queue-drain + Почти неблокирующий
  • 153. Что там под капотом 153 Serialization. Queue-drain + Почти неблокирующий ● Работает внутри RxJava version 2.X
  • 154. Выводы 154 Выдохнули :) ● Знаем, как выглядит Rx снаружи ● Понимаем, что внутри Rx
  • 155. А дальше? 155 А как же передача данных?
  • 157. А дальше? 157 Клиент все равно ждет Client Client Client Handler Thread Handler Thread Handler Thread + Rx chain Rx chain Rx chain
  • 158. А дальше? 158 Клиент все равно ждет Client Client Client Handler Thread Handler Thread Handler Thread + Rx chain Rx chain Rx chain
  • 159. А дальше? 159 Канал данных не завершен
  • 160. А дальше? 160 Дальнейшие шаги ● Chunked Transfer Encoding для передачи данных по частям
  • 161. А дальше? 161 Дальнейшие шаги ● Chunked Transfer Encoding для передачи данных по частям ● Non-blocking NIO и Async Servlet
  • 162. Выводы 162 Выводы ● Базовый набор операторов RxJava прост и удобен для построения цепочек сбора и обработки данных
  • 163. Выводы 163 Выводы ● Базовый набор операторов RxJava прост и удобен для построения цепочек сбора и обработки данных ● Писать свои операторы просто, но надо понимать механику, ошибки могут запомниться вам надолго
  • 164. Выводы 164 Выводы ● Базовый набор операторов RxJava прост и удобен для построения цепочек сбора и обработки данных ● Писать свои операторы просто, но надо понимать механику, ошибки могут запомниться вам надолго ● Rx-цепочки внутри синхронного приложения не являются полноценным решением
  • 165. Почитать / Посмотреть 165 Почитать / Посмотреть ● http://www.reactivemanifesto.org/ - прочитать и подписать ● https://github.com/reactive-streams/reactive-streams-jvm/ ● http://reactivex.io/ - документация ReactiveX ● https://github.com/ReactiveX/RxJava/wiki - wiki проекта ● http://akarnokd.blogspot.ru/ - блог о внутренностях RxJava ● http://tomstechnicalblog.blogspot.ru/ - понятные объяснения