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

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

Kirill Tolkachev
April 05, 2019
730

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 Slide

  2. @tolkv
    @lavcraft
    @jekaborisov
    @jeka1978

    View Slide

  3. Spring
    Reactive
    Or Not Reactive

    View Slide

  4. Задача

    View Slide

  5. Задача

    View Slide

  6. Задача

    View Slide

  7. Задача

    View Slide

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

    View Slide

  9. View Slide

  10. View Slide

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

    View Slide

  12. View Slide

  13. View Slide

  14. View Slide

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

    View Slide

  16. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. View Slide

  24. View Slide

  25. ...

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. Demo
    Manual backpressure

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  48. Webflux

    View Slide

  49. Spring Webflux & Project Reactor

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  53. exchange
    WebClient
    subscribe
    Flux
    Controller
    http connection

    View Slide

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

    View Slide

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

    View Slide

  56. Demo
    Webflux

    View Slide

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

    View Slide

  58. 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 Slide

  59. 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 Slide

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

    View Slide

  61. subscribe( )
    Subscriber
    Flux
    reactor-netty
    LetterController

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  72. Demo
    Webflux #2

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  82. Demo
    Webflux #3

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  90. View Slide

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

    View Slide

  92. Почувствуйте разницу
    [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 Slide

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

    View Slide

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

    View Slide

  95. 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 Slide

  96. 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 Slide

  97. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  101. 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 Slide

  102. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  106. Хочу Backpressure

    View Slide

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

    View Slide

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

    View Slide

  109. Demo
    Webflux #3

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  117. Выводы – кривая обучения 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 Slide

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

    View Slide

  119. Вопросы

    View Slide

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

    View Slide

  121. Ссылки
    Код с демо
    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 Slide