77
Reactive Streams Алексей Романчук

Codefest-2015 Reactive Streams

Embed Size (px)

Citation preview

Page 1: Codefest-2015 Reactive Streams

Reactive StreamsАлексей Романчук

Page 2: Codefest-2015 Reactive Streams

2Обо мне

Page 3: Codefest-2015 Reactive Streams

10k msg/s

Page 4: Codefest-2015 Reactive Streams

4Эволюция backendов

DBApp

Page 5: Codefest-2015 Reactive Streams

5Эволюция backendов

DB

API

Storage

Cache

App

App

Page 6: Codefest-2015 Reactive Streams

Высокопроизводительный backend

Page 7: Codefest-2015 Reactive Streams

60мс

Page 8: Codefest-2015 Reactive Streams

• парсинг запроса - 1мс

• проверка в кеше - 2мс

• запрос в БД - 30мс

• запрос в АПИ - 25мс

• формирование ответа - 2мс

860мс

Page 9: Codefest-2015 Reactive Streams

9

Page 10: Codefest-2015 Reactive Streams

• Отдельный поток

• 95% ожидает

• Занимает ресурсы

10Синхронная модель

Page 11: Codefest-2015 Reactive Streams

11

Ожидание

Page 12: Codefest-2015 Reactive Streams

• Обработка в разных потоках

• Потоки не простаивают

• Эффективное использование ресурсов

12Асинхронная модель

Page 13: Codefest-2015 Reactive Streams

• БД

• Внешние API

• Сеть

• Взаимодействие с другими потокам

13Асинхронные границы везде

Page 14: Codefest-2015 Reactive Streams

• Композиция

• Ветвление

• Обработка ошибок

• Backpressure

14Асинхронность это сложно

Page 15: Codefest-2015 Reactive Streams

15Асинхронность это сложно

Page 16: Codefest-2015 Reactive Streams

16Асинхронность это сложно

Page 17: Codefest-2015 Reactive Streams

17Асинхронность это сложно

Page 18: Codefest-2015 Reactive Streams

• Mutex, semaphore, etc

• Green threads

• Future-Promise

• Потоки данных

18Модели

Page 19: Codefest-2015 Reactive Streams

Потоки

Page 20: Codefest-2015 Reactive Streams

20

Page 21: Codefest-2015 Reactive Streams

“В одну реку нельзя войти дважды”

Page 22: Codefest-2015 Reactive Streams

• Множество сообщений одного типа

• Зависит от времени начала наблюдения

• Может не иметь ни начала ни конца

22Потоки данных

Page 23: Codefest-2015 Reactive Streams

• curl twitter.com | grep CodeFest | wc -n

• сетевые соединения

• звук и видео

• запросы и ответы сервера

23Потоки вокруг нас

Page 24: Codefest-2015 Reactive Streams

24Backend

Запрос ОтветМАГИЯЗапросЗапрос ОтветОтвет

Page 25: Codefest-2015 Reactive Streams

25

Page 26: Codefest-2015 Reactive Streams

26Backend

Парсинг БД API Ответ

Парсинг БД API Ответ

Page 27: Codefest-2015 Reactive Streams

27Backend

Парсинг БД API Ответ

Парсинг БД API Ответ

Page 28: Codefest-2015 Reactive Streams

28Потоки данных

Парсинг БД API Ответ

Page 29: Codefest-2015 Reactive Streams

• Простые асинхронные границы

• Картина в целом

• Параллельное программирование

• Ошибки и завершение

29Преимущества

Page 30: Codefest-2015 Reactive Streams

Backpressure

Page 31: Codefest-2015 Reactive Streams

31Событий слишком много

Отправитель Получатель

Page 32: Codefest-2015 Reactive Streams

32Событий слишком много

Отправитель Получатель

Page 33: Codefest-2015 Reactive Streams

33Событий слишком много

Отправитель Получатель

Page 34: Codefest-2015 Reactive Streams

34Событий слишком много

Отправитель Получатель

Page 35: Codefest-2015 Reactive Streams

35Событий слишком много

Отправитель Получатель

Page 36: Codefest-2015 Reactive Streams

36Событий слишком много

Отправитель Получатель

Page 37: Codefest-2015 Reactive Streams

37Событий слишком много

Отправитель Получатель

Page 38: Codefest-2015 Reactive Streams

38Событий слишком много

Отправитель Получатель

Page 39: Codefest-2015 Reactive Streams

39Блокирующий вызов

Отправитель Получатель

Page 40: Codefest-2015 Reactive Streams

40Pull

Отправитель Получатель

Page 41: Codefest-2015 Reactive Streams

41Negative Acknowledge

Отправитель Получатель

Page 42: Codefest-2015 Reactive Streams

42Negative Acknowledge

Отправитель Получатель

Page 43: Codefest-2015 Reactive Streams

43Dynamic pull-push

Отправитель Получатель

4

Page 44: Codefest-2015 Reactive Streams

44Dynamic pull-push

4

Отправитель Получатель

Page 45: Codefest-2015 Reactive Streams

45Dynamic pull-push

3

Отправитель Получатель

Page 46: Codefest-2015 Reactive Streams

46Dynamic pull-push

3

Отправитель Получатель

Page 47: Codefest-2015 Reactive Streams

47Dynamic pull-push

0

Отправитель Получатель

Page 48: Codefest-2015 Reactive Streams

48Dynamic pull-push

0

Отправитель Получатель

Page 49: Codefest-2015 Reactive Streams

49Dynamic pull-push

0

Отправитель Получатель

2

Page 50: Codefest-2015 Reactive Streams

50Dynamic pull-push

2

Отправитель Получатель

Page 51: Codefest-2015 Reactive Streams

51Dynamic pull-push

0

Отправитель Получатель

Page 52: Codefest-2015 Reactive Streams

ОтветБДПарсинг

52Все в сборе

Page 53: Codefest-2015 Reactive Streams

Reactive Streams

Page 54: Codefest-2015 Reactive Streams

• Спецификация

• Асинхронного взаимодействия

• Упорядоченные сообщения

• Обратная связь

54Reactive Streams

Page 55: Codefest-2015 Reactive Streams

• RxJava

• Akka Stream

• Reactor

• Ratpack

55Реализации

Page 56: Codefest-2015 Reactive Streams

“Talk is cheap, show me the code”

Page 57: Codefest-2015 Reactive Streams

• Reactive Stream

• Dynamic pull-push model

• Scala, Java

• Статическая типизация

• Модель акторов

57Akka Stream

Page 58: Codefest-2015 Reactive Streams

* <img width="640" height="430" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/repeatWhen.f.png" alt=""> * <dl> * <dt><b>Scheduler:</b></dt> * <dd>you specify which {@link Scheduler} this operator will use</dd> * </dl> * * @param notificationHandler * receives an Observable of notifications with which a user can complete or error, aborting the repeat. * @param scheduler * the {@link Scheduler} to emit the items on * @return the source Observable modified with repeat logic * @see <a href="http://reactivex.io/documentation/operators/repeat.html">ReactiveX operators documentation: Repeat</a> */public final Observable<T> repeatWhen(final Func1<? super Observable<? extends Void>, ? extends Observable<?>> notificationHandler, Scheduler scheduler) { Func1<? super Observable<? extends Notification<?>>, ? extends Observable<?>> dematerializedNotificationHandler = new Func1<Observable<? extends Notification<?>>, Observable<?>>() { @Override public Observable<?> call(Observable<? extends Notification<?>> notifications) { return notificationHandler.call(notifications.map(new Func1<Notification<?>, Void>() { @Override public Void call(Notification<?> notification) { return null; } })); } }; return OnSubscribeRedo.repeat(this, dematerializedNotificationHandler, scheduler);} /** * Returns an Observable that emits the same values as the source Observable with the exception of an * {@code onCompleted}. An {@code onCompleted} notification from the source will result in the emission of * a {@code void} item to the Observable provided as an argument to the {@code notificationHandler} * function. If that Observable calls {@code onComplete} or {@code onError} then {@code repeatWhen} will * call {@code onCompleted} or {@code onError} on the child subscription. Otherwise, this Observable will * resubscribe to the source observable. * <p> * <img width="640" height="430" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/repeatWhen.f.png" alt=""> * <dl> * <dt><b>Scheduler:</b></dt> * <dd>{@code repeatWhen} operates by default on the {@code trampoline} {@link Scheduler}.</dd> * </dl> * * @param notificationHandler * receives an Observable of notifications with which a user can complete or error, aborting the repeat. * @return the source Observable modified with repeat logic * @see <a href="http://reactivex.io/documentation/operators/repeat.html">ReactiveX operators documentation: Repeat</a> */public final Observable<T> repeatWhen(final Func1<? super Observable<? extends Void>, ? extends Observable<?>> notificationHandler) { Func1<? super Observable<? extends Notification<?>>, ? extends Observable<?>> dematerializedNotificationHandler = new Func1<Observable<? extends Notification<?>>, Observable<?>>() { @Override public Observable<?> call(Observable<? extends Notification<?>> notifications) { return notificationHandler.call(notifications.map(new Func1<Notification<?>, Void>() { @Override public Void call(Notification<?> notification) { return null; } })); } }; return OnSubscribeRedo.repeat(this, dematerializedNotificationHandler);} /** * An Observable that never sends any information to an {@link Observer}. * This Observable is useful primarily for testing purposes. * * @param <T> * the type of item (not) emitted by the Observable */private static class NeverObservable<T> extends Observable<T> { public NeverObservable() { super(new OnSubscribe<T>() { @Override public void call(Subscriber<? super T> observer) { // do nothing } }); }} /** * An Observable that invokes {@link Observer#onError onError} when the {@link Observer} subscribes to it. * * @param <T> * the type of item (ostensibly) emitted by the Observable */private static class ThrowObservable<T> extends Observable<T> { public ThrowObservable(final Throwable exception) { super(new OnSubscribe<T>() { /** * Accepts an {@link Observer} and calls its {@link Observer#onError onError} method. * * @param observer * an {@link Observer} of this Observable */ @Override public void call(Subscriber<? super T> observer) {

/** * Operator function for lifting into an Observable. */ public interface Operator<R, T> extends Func1<Subscriber<? super R>, Subscriber<? super T>> { // cover for generics insanity } /** * Lifts a function to the current Observable and returns a new Observable that when subscribed to will pass * the values of the current Observable through the Operator function. * <p> * In other words, this allows chaining Observers together on an Observable for acting on the values within * the Observable. * <p> {@code * observable.map(...).filter(...).take(5).lift(new OperatorA()).lift(new OperatorB(...)).subscribe() * } * <p> * If the operator you are creating is designed to act on the individual items emitted by a source * Observable, use {@code lift}. If your operator is designed to transform the source Observable as a whole * (for instance, by applying a particular set of existing RxJava operators to it) use {@link #compose}. * <dl> * <dt><b>Scheduler:</b></dt> * <dd>{@code lift} does not operate by default on a particular {@link Scheduler}.</dd> * </dl> * * @param lift the Operator that implements the Observable-operating function to be applied to the source * Observable * @return an Observable that is the result of applying the lifted Operator to the source Observable * @see <a href="https://github.com/ReactiveX/RxJava/wiki/Implementing-Your-Own-Operators">RxJava wiki: Implementing Your Own Operators</a> */ public final <R> Observable<R> lift(final Operator<? extends R, ? super T> lift) { return new Observable<R>(new OnSubscribe<R>() { @Override public void call(Subscriber<? super R> o) { try { Subscriber<? super T> st = hook.onLift(lift).call(o); try { // new Subscriber created and being subscribed with so 'onStart' it st.onStart(); onSubscribe.call(st); } catch (Throwable e) { // localized capture of errors rather than it skipping all operators // and ending up in the try/catch of the subscribe method which then // prevents onErrorResumeNext and other similar approaches to error handling if (e instanceof OnErrorNotImplementedException) { throw (OnErrorNotImplementedException) e; } st.onError(e); } } catch (Throwable e) { if (e instanceof OnErrorNotImplementedException) { throw (OnErrorNotImplementedException) e; } // if the lift function failed all we can do is pass the error to the final Subscriber // as we don't have the operator available to us o.onError(e); } } }); } /** * Transform an Observable by applying a particular Transformer function to it. * <p> * This method operates on the Observable itself whereas {@link #lift} operates on the Observable's * Subscribers or Observers. * <p> * If the operator you are creating is designed to act on the individual items emitted by a source * Observable, use {@link #lift}. If your operator is designed to transform the source Observable as a whole * (for instance, by applying a particular set of existing RxJava operators to it) use {@code compose}. * <dl> * <dt><b>Scheduler:</b></dt> * <dd>{@code compose} does not operate by default on a particular {@link Scheduler}.</dd> * </dl> * * @param transformer implements the function that transforms the source Observable * @return the source Observable, transformed by the transformer function * @see <a href="https://github.com/ReactiveX/RxJava/wiki/Implementing-Your-Own-Operators">RxJava wiki: Implementing Your Own Operators</a> */ @SuppressWarnings("unchecked") public <R> Observable<R> compose(Transformer<? super T, ? extends R> transformer) { return ((Transformer<T, R>) transformer).call(this); } /** * Transformer function used by {@link #compose}. * @warn more complete description needed */ public static interface Transformer<T, R> extends Func1<Observable<T>, Observable<R>> { // cover for generics insanity } /* ********************************************************************************************************* * Operators Below Here * ********************************************************************************************************* */

Page 59: Codefest-2015 Reactive Streams

59Ключевые абстракции

Source SinkFlow

Page 60: Codefest-2015 Reactive Streams

60Ключевые абстракции

Source SinkFlow

Page 61: Codefest-2015 Reactive Streams

61Sourceval iterableSource = Source(1 to 50) val tickSource = Source(1 second, 1 second, "Tick") val singleSource = Source.single("CodeFest") val emptySource = Source.empty() val zmqSource = ???

Page 62: Codefest-2015 Reactive Streams

62Sinkval blackhole = Sink.ignoreval onComplete = Sink.onComplete { result => System.exit(0) } val foreach = Sink.foreach(println) val firstElement = Sink.head[Int]

Page 63: Codefest-2015 Reactive Streams

63Flowimplicit val as = ActorSystem("CodeFest") implicit val materializer = ActorFlowMaterializer()val source = Source(1 to 50) val sink = Sink.foreach[Int](println) val flow = source.to(sink) flow.run()

Page 64: Codefest-2015 Reactive Streams

64Flowval flow2 = source .map { x => x * 2 } .filter { x => x % 3 == 0 } .to(sink) flow2.run()

Page 65: Codefest-2015 Reactive Streams

65Flowval source = Source(1 to 50) val sink = Sink.foreach[String](println) val flow2 = source .map { x => x.toString } .map { x => x / 13 } .to(sink)flow2.run()

Page 66: Codefest-2015 Reactive Streams

66Flow

Парсинг БД API Ответ

Page 67: Codefest-2015 Reactive Streams

67Flowval request: Source[Request] = ???def parser: Request => Query = ???def dbCall: Query => Future[List[Int]] = ???def apiCall: List[Int] => Future[List[String]] = ???def buildResponse: List[String] => Response = ???

val flow3 = request .map(parser) .mapAsync(dbCall) .mapAsync(apiCall) .map(buildResponse) .to(response)

Page 68: Codefest-2015 Reactive Streams

• MQ

• Потоки данных (события, метрики, файлы, видео)

• UI

• Очереди задач

68Применения

Page 69: Codefest-2015 Reactive Streams

69Adopters

Page 70: Codefest-2015 Reactive Streams

• C#

• Java, Scala

• JavaScript

• Objective-C

• Python

• Ruby

• PHP

70Языки программирования

Page 71: Codefest-2015 Reactive Streams

Мир изменился

Page 75: Codefest-2015 Reactive Streams

• Reactive Extensions

• Reactive Extensions for JavaScript

• Reactive Cocoa

• Rx.py

• Rx.rb

• Rx.php

75Ссылки

Page 76: Codefest-2015 Reactive Streams

• drop, take

• group, mapConcat

• grouped, flatten

• buffer, conflate, expand

76Flow

Page 77: Codefest-2015 Reactive Streams

• Balance, Broadcast, Merge

• Zip, Unzip

• FlexiMerge, FlexiRoute

77Graph