Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reactive или не reactive, вот в чем вопрос

Kirill Tolkachev
April 05, 2019
880

Reactive или не reactive, вот в чем вопрос

Доклад Reactive or Not Reactive c JPoint 2019 – https://jpoint.ru/talks/b9ib3swihayyhnnpjghc2/

Все вы слышали фразу: «Человек, научившийся пользоваться молотком, во всем начинает видеть гвоздь». В мире программирования это очень часто происходит с новыми и модными технологиями, которые далеко не всегда применяются по назначению. В результате мы имеем более сложный API, который могут поддерживать только прошедшие медные трубы программисты, кучу багов и прочих проблем.

Как нам избежать сломанных пальцев и разбитых молотком вещей при попытке внедрить React?

Мы рассмотрим пример системы, в которой есть проблемы, и, конечно же, попробуем отрефакторить её в реактивном стиле. Рассмотрим преимущества и недостатки не только подхода, но и API конкретных реализаций. Оценим сложность, которая была до рефакторинга, и ту сложность, которую мы привнесли после. Постараемся разобраться, что игрушки, а что нет.

Код с демо — https://github.com/lavcraft/spring-react-or-not-react-jpoint2019
Json streaming — https://en.wikipedia.org/wiki/JSON_streaming
Jackson Smile — https://github.com/FasterXML/jackson-dataformats-binary/tree/master/smile

WebClient Java 11 HttpClient Support — https://github.com/spring-projects/spring-framework/issues/21014
Webflux and RSocket backpressure — https://stackoverflow.com/questions/52244808/backpressure-mechanism-in-spring-web-flux/52245213#52245213

Kirill Tolkachev

April 05, 2019
Tweet

Transcript

  1. Spring
    Reactive Or Not Reactive

    1984

    View full-size slide

  2. @tolkv
    @lavcraft
    @jekaborisov
    @jeka1978

    View full-size slide

  3. Spring
    Reactive
    Or Not Reactive

    View full-size slide

  4. Задача

    View full-size slide

  5. Задача

    View full-size slide

  6. Задача

    View full-size slide

  7. Задача

    View full-size slide

  8. Задача
    Имя: Папа
    Фамилия: Папа мальчика
    Должность: Академик

    View full-size slide

  9. Задача
    Имя: Папа
    Фамилия: Папа мальчика
    Должность: Академик

    View full-size slide

  10. Архитектура проекта «Big Brother»
    Перехватчик писем,
    отсылает все письма на распознание
    Правоохранительные органы,
    принимают решение об аресте
    Расшифровщик писем,
    Выясняет чьё письмо о отсылает
    в Правоохранительные органы

    View full-size slide

  11. Demo
    Наивная имплементация #0

    View full-size slide

  12. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2

    View full-size slide

  13. Tomcat
    Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Buffer #T1
    Wait

    View full-size slide

  14. Tomcat
    Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Buffer #T1
    Wait

    View full-size slide

  15. Tomcat
    Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Buffer #T1
    Reject

    View full-size slide

  16. Demo
    Наивная имплементация #1

    View full-size slide

  17. О пропускной способности

    View full-size slide

  18. pechkin-service big-brother-service agent-smith-service
    request(n)
    request(n)
    Buffer #P0 Buffer #P1 Buffer #P2
    1t 8t 2t
    ∞ 8rps 2rps

    View full-size slide

  19. pechkin-service big-brother-service agent-smith-service
    request(n)
    request(n)
    Buffer #P0 Buffer #P1 Buffer #P2
    1t 8t 2t
    ∞ 8rps 2rps

    View full-size slide

  20. pechkin-service big-brother-service agent-smith-service
    request(n)
    request(n)
    Buffer #P0 Buffer #P1 Buffer #P2
    1t 8t 2t
    ∞ 8rps 2rps

    View full-size slide

  21. Задача
    Оптимизировать количество запросов под
    ресурсы в цепочке «Backpressure»

    View full-size slide

  22. Demo
    Manual backpressure

    View full-size slide

  23. pechkin-service big-brother-service agent-smith-service
    request(n)
    request(n)
    Buffer #P0 Buffer #P1 Buffer #P2
    1t 8t 2t
    ∞ 8rps 2rps

    View full-size slide

  24. Варианты получать данные

    View full-size slide

  25. Что должен делать producer когда consumer не
    справляется
    Продюсер читает
    данные с нужной
    скоростью
    Нет проблемы
    В продюсер текут данные
    Всё что не успеваем
    отослать, суем в
    очередь, когда нет
    места в очереди,
    дропаем, то что
    менее важно

    View full-size slide

  26. Producer
    Middlewares
    events Buffer #P0
    events
    Consumer #0
    Consumer #1
    Buffer #C2
    Buffer #C2
    Buffer #M1

    View full-size slide

  27. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0

    View full-size slide

  28. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0

    View full-size slide

  29. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer
    Or
    batch single
    Buffer #C2

    View full-size slide

  30. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer
    Or
    batch single
    Buffer #C2

    View full-size slide

  31. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2

    View full-size slide

  32. Tomcat
    Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Buffer #T1
    Wait

    View full-size slide

  33. Tomcat
    Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Buffer #T1
    Wait

    View full-size slide

  34. Tomcat
    Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Buffer #T1
    Reject

    View full-size slide

  35. Push and Pull semantics related to consumer
    events
    Pusher Buffer #P0
    Passive Consumer Buffer #C2
    Black Hole

    View full-size slide

  36. Push and Pull semantics related to consumer
    events
    Pull & Push Service
    Passive Consumer Buffer #C2
    Black Hole
    Buffer #P0

    View full-size slide

  37. Push and Pull semantics related to consumer
    events
    Pull & Push Service
    Passive Consumer Buffer #C2
    Black Hole
    Buffer #P0

    View full-size slide

  38. Push and Pull semantics related to consumer
    events
    Pull & Push Service
    Passive Consumer Buffer #C2
    Black Hole
    Buffer #P0

    View full-size slide

  39. Spring Webflux & Project Reactor

    View full-size slide

  40. Spring Webflux & Project Reactor
    Spring MVC (Servlet 3.1+)
    or
    Spring Webflux (default)

    View full-size slide

  41. Подключаем webflux
    Было 'org.springframework.boot:spring-boot-starter-web'
    Стало 'org.springframework.boot:spring-boot-starter-webflux'

    View full-size slide

  42. WebClient is a new RestTemplate
    Было
    RestTemplateBuilder → RestTemplate
    RestTemplate — передаём статический текс в body
    Стало
    WebClient.Builder → WebClient
    WebClient — можем передать последовательность Flux в качестве body

    View full-size slide

  43. exchange
    WebClient
    subscribe
    Flux
    Controller
    http connection

    View full-size slide

  44. exchange
    WebClient
    reactor-netty jetty-client Java 11 http client
    Issue 19658
    subscribe
    Issue 21014
    Flux
    closed open
    Controller
    http connection

    View full-size slide

  45. Letter generator
    Flux.generate(
    sink -> sink.next(randomLetter())
    );

    View full-size slide

  46. WebFlux #1
    public void processLetter(@RequestBody Flux f)
    400 BAD_REQUEST "Request body is missing

    View full-size slide

  47. void processLetter(Flux f)
    f.doOnNext()
    .doOnNext()
    .log()
    .subscribe()
    return Mono.empty() / nothing / anything
    LetterController
    Close connection
    400 BAD_REQUEST "Request body is missing

    View full-size slide

  48. void processLetter(Flux f)
    f.doOnNext()
    .doOnNext()
    .log()
    .subscribe()
    return Mono.empty() / nothing / anything
    LetterController
    Close connection
    400 BAD_REQUEST "Request body is missing
    Для начала вычитывания

    View full-size slide

  49. Flux пайплайн жизненный цикл
    1. Assembly
    Как new Builder().prop().build()
    Только Flux.create().subscribe(subscriber)
    2. В subscriber.onSubscribe() передаётся подписка – subscription
    3. Через subscription выполняется первый запрос на ивенты или отписка

    View full-size slide

  50. subscribe( )
    Subscriber
    Flux
    reactor-netty
    LetterController

    View full-size slide

  51. Flux
    .subscribe( )
    Subscriber
    subscription.request(N
    )
    → onSubscribe(subscription)
    onNext( )
    .map( )
    .filter( )
    reactor-netty
    LetterController

    View full-size slide

  52. Flux
    .subscribe( )
    Subscriber
    subscription.request(N)
    → onSubscribe(subscription)
    onNext( )
    .map( )
    .filter( )
    reactor-netty
    LetterController

    View full-size slide

  53. Flux
    .subscribe( )
    Subscriber
    subscription.request(N)
    → onSubscribe(subscription)
    onNext( )
    .map( )
    .filter( )
    reactor-netty
    LetterController

    View full-size slide

  54. Flux
    .subscribe( )
    Subscriber
    subscription.request(N)
    → onSubscribe(subscription)
    onNext( )
    .map( )
    .filter( )
    reactor-netty
    LetterController

    View full-size slide

  55. Flux
    .subscribe( )
    Subscriber
    subscription.request(N)
    → onSubscribe(subscription)
    onNext( )
    .map( )
    .filter( )
    reactor-netty
    LetterController

    View full-size slide

  56. onComplete( )
    Flux
    .subscribe( )
    Subscriber
    subscription.request(N)
    → onSubscribe(subscription)
    onNext( )
    .map( )
    .filter( )
    onError( )
    reactor-netty
    LetterController

    View full-size slide

  57. WebFlux #1
    public Mono processLetter(@RequestBody Flux f)
    Mono.empty()
    Mono.never()

    View full-size slide

  58. Mon processLetter(Flux f)
    f.doOnNext()
    .doOnNext()
    .log()
    .subscribe()
    return Mono.never()
    LetterController
    NO Close connection
    reactor-netty

    View full-size slide

  59. Немного о HTTP
    request
    GET / HTTP 1/1
    HOST: dfsdfg
    \n\r
    response

    View full-size slide

  60. Mon processLetter(Flux f)
    return f.doOnNext()
    .doOnNext()
    .log()
    .then()
    LetterController
    Close connection
    Mono.onComplete()
    reactor-netty

    View full-size slide

  61. Demo
    Webflux #2

    View full-size slide

  62. network
    Чувак, где мои письма
    Flux
    request(unbounded)

    View full-size slide

  63. У нас же есть Backpressure из коробки!

    View full-size slide

  64. network
    doOnNext
    Чувак, где мои письма
    Flux
    request(unbounded)

    View full-size slide

  65. network
    doOnNext
    Чувак, где мои письма – в буферах!
    Flux
    request(unbounded)

    View full-size slide

  66. Нас засудят за сексизм!

    View full-size slide

  67. Нас засудят за сексизм!

    View full-size slide

  68. network
    doOnNext
    Чувак/чувиха, где мои письма – в буферах!
    FlatMapFlux
    request(8)
    Ограничили рост буфера

    View full-size slide

  69. Чувак, почему так медленно?
    1000rps vs 2rps
    ...
    doOnNext
    doOnNext(decode)
    1ms
    1ms
    250ms
    ...

    View full-size slide

  70. Чувак, где мои письма
    ...
    doOnNext
    doOnNext(decode)
    ...
    doOnNext(decode) doOnNext(decode)

    View full-size slide

  71. Demo
    Webflux #3

    View full-size slide

  72. Mon processLetter(Flux f)
    return f.doOnNext()
    .flatMap(
    letter -> Mono.fromCallable(() -> decode(letter))
    .subscribeOn(fromExecutor(threadPool)),
    threadPool.getMaximumPoolSize())
    .log()
    .then()
    LetterController
    reactor-netty

    View full-size slide

  73. network
    doOnNext
    Чувак, где мои письма
    Flux
    request(unbounded)
    Drop?
    BP сработал
    Без flatMap

    View full-size slide

  74. network
    doOnNext
    Чувак, где мои письма
    FlatMapFlux
    request(8)
    c flatMap

    View full-size slide

  75. А что с нашими DirectBuffer`ами? netty

    View full-size slide

  76. А что с нашими DirectBuffer`ами? netty
    java.lang.OutOfMemoryError: Direct buffer memory
    at j.n.Bits.reserveMemory(Bits.java:175)
    at j.n.DirectByteBuffer.(DirectByteBuffer.java:118)

    View full-size slide

  77. Что произошло и как чинить?

    View full-size slide

  78. Случился TCP Backpressure

    View full-size slide

  79. Как то так, через хитро
    закрученную жопу
    оно и работает

    View full-size slide

  80. Почувствуйте разницу
    [letter-4] onNext
    [letter-4] onNext
    [letter-2] onNext
    [letter-1] onNext
    [letter-4] onNext
    [letter-3] onNext
    [reactor-http-nio-3] onNext
    [reactor-http-nio-3] onNext
    [reactor-http-nio-3] onNext
    [reactor-http-nio-3] onNext
    [reactor-http-nio-3] onNext
    [reactor-http-nio-3] onNext
    vs

    View full-size slide

  81. TCP BP
    Без flatMap #0
    flux netty netty flux
    reactor-netty
    reactor-nio-1
    Без flatMap
    Пулы DirectBuffer`ов netty
    socket

    View full-size slide

  82. TCP BP
    Без flatMap #1
    flux netty netty flux
    reactor-netty
    reactor-nio-1
    Пулы DirectBuffer`ов netty
    socket
    1. Копирует данные из cистемного сокета в
    DirectBuffer

    View full-size slide

  83. TCP BP
    Без flatMap #2
    flux netty netty flux
    reactor-netty
    reactor-nio-1
    Пулы DirectBuffer`ов netty
    socket
    1. Копирует данные из cистемного сокета в
    DirectBuffer
    2. Вызывает наш медленный метод
    decoder.decode LetterController.java

    View full-size slide

  84. TCP BP
    Без flatMap #3
    flux netty netty flux
    reactor-netty
    reactor-nio-1
    Пулы DirectBuffer`ов netty
    socket
    1. Копирует данные из cистемного сокета в
    DirectBuffer
    2. Вызывает наш медленный метод
    decoder.decode LetterController.java
    3. Копирует данные из cистемного сокета в
    DirectBuffer

    View full-size slide

  85. TCP BP
    Без flatMap #3
    flux netty netty flux
    reactor-netty
    reactor-nio-1
    Пулы DirectBuffer`ов netty
    socket
    1. Копирует данные из cистемного сокета в
    DirectBuffer
    2. Вызывает наш медленный метод
    decoder.decode LetterController.java
    3. Копирует данные из cистемного сокета в
    DirectBuffer
    4. ... и так повторяется

    View full-size slide

  86. TCP BP
    После добавления
    flatMap
    flux netty netty flux
    reactor-netty
    reactor-nio-1 letter-N
    Пулы DirectBuffer`ов netty
    socket
    1. Копирует данные из cистемного сокета в [1ms]
    DirectBuffer

    View full-size slide

  87. TCP BP
    После добавления
    flatMap
    flux netty netty flux
    reactor-netty
    reactor-nio-1 letter-N
    Пулы DirectBuffer`ов netty
    socket
    1. Вызывает наш медленный метод [1000ms]
    decoder.decode LetterController.java

    View full-size slide

  88. TCP BP
    После добавления
    flatMap
    flux netty netty flux
    reactor-netty
    reactor-nio-1 letter-N
    Пулы DirectBuffer`ов netty
    socket
    1. Копирует данные из cистемного сокета в [1ms]
    DirectBuffer
    [1000ms]

    View full-size slide

  89. TCP BP
    После добавления
    flatMap
    flux netty netty flux
    reactor-netty
    reactor-nio-1 letter-N
    Пулы DirectBuffer`ов netty
    socket
    [1000ms]
    letter-N
    Копирует данные
    из cистемного сокета
    в DirectBuffer
    [1ms]
    Вызывает наш медленный
    метод decoder.decode
    LetterController.jav
    a
    [1000ms]
    работает независимо

    View full-size slide

  90. TCP BP
    После добавления
    flatMap
    flux netty netty flux
    reactor-netty
    reactor-nio-1 letter-N
    Пулы DirectBuffer`ов netty
    socket
    Копирует данные
    из cистемного сокета
    в DirectBuffer
    [1ms]
    [1000ms]
    Вызывает наш медленный
    метод decoder.decode
    LetterController.jav
    a
    [1000ms]
    работает независимо

    View full-size slide

  91. А что с нашими DirectBuffer`ами? netty
    java.lang.OutOfMemoryError: Direct buffer memory
    at j.n.Bits.reserveMemory(Bits.java:175)
    at j.n.DirectByteBuffer.(DirectByteBuffer.java:118)

    View full-size slide

  92. У нас же есть Backpressure из коробки!

    View full-size slide

  93. У нас же есть Backpressure из коробки!
    Нету, сайд
    эффект

    View full-size slide

  94. Хочу Backpressure

    View full-size slide

  95. Да придёт спаситель
    RSocket

    View full-size slide

  96. Кто такое RSocket
    см https://jpoint.ru/talks/2xelfl2d9mgyweeeq60wiw/

    View full-size slide

  97. Demo
    Webflux #3

    View full-size slide

  98. RSocket идеальное решение?
    Можно было бы его продать так же как webflux :)
    Но
    1. Версия у него 0.11
    2. Пока делали доклад нашли 2 бага. Спасибо Олегу, смогли обойти их
    3. Привычные вещи не всегда делаются просто
    4. И это только только то что мы успели найти ...

    View full-size slide

  99. Выводы ложные
    1. К чёрту велосипеды
    2. К чёрту webflux
    3. Да здравствует RSocket

    View full-size slide

  100. Выводы ложные
    1. К чёрту велосипеды
    2. К чёрту webflux
    3. Да здравствует RSocket

    View full-size slide

  101. Сияющие технологии
    1. В каких то задачах webflux будет хорош
    2. В каких то задачах RSocket будет плох
    3. Но своё решение всегда как минимум заставляет задуматься

    View full-size slide

  102. Сияющие технологии
    1. В каких то задачах webflux будет хорош
    2. В каких то задачах RSocket будет плох
    3. Но своё решение всегда как минимум заставляет задуматься

    View full-size slide

  103. Выводы
    1. Не всё то Reactive что с интегрировано с Project Reactor
    a. Поддержка Backpressure на основе TCP BP имеет сайд эффекты
    b. Control flow между сервисами для реактивного пайплайна так же важен
    2. Webflux не подходит для балансировки скорости между сервисами
    3. С RSocket честный backpressure, пока сырой, но перспективный
    4.

    View full-size slide

  104. Настоящие выводы
    Не стоит внедрять технологии на основе авторитета:
    1. спикера
    2. компании
    3. Тренда
    Webflux хорош когда нужно сэкономить коннекшены и отправить пачку
    данных
    RSocket хорош во всех наших кейсах
    Свой велосипед всегда поняитнее

    View full-size slide

  105. Выводы – кривая обучения reactive
    effort/time
    Holy moly
    I am god!
    Wow
    It’s works
    This doesnt
    work
    Why can’t i get
    it to work
    Learn
    about
    reactive
    mastery

    View full-size slide

  106. Выводы – кривая обучения reactive
    effort/time
    Learn
    about
    reactive
    mastery

    View full-size slide

  107. Вопросы

    View full-size slide

  108. @tolkv
    @lavcraft
    @jekaborisov
    @jeka1978
    Вопросы

    View full-size slide

  109. Ссылки
    Код с демо
    1. https://github.com/lavcraft/spring-react-or-not-react-jpoint2019
    Spring Webflux Streaming
    1. Json streaming
    2. Jackson Smile
    Spring WebClient
    1. WebClient Java 11 HttpClient Support
    2. Webflux and RSocket backpressure

    View full-size slide