$30 off During Our Annual Pro Sale. View Details »

Spring Boot Reactive Ripper Joker 2019

Spring Boot Reactive Ripper Joker 2019

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

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

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

Kirill Tolkachev

October 24, 2019
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring
    Reactive Or Not Reactive

    1984

    View Slide

  2. Улучшения
    1. Начало перенести в слайды и дропнуть демку ради упростить
    2. Упростить, чтобы былее очевидно ЗАЧЕМ ЭТО может понадобиться.
    Кажется кейсы Мы сначала сделали а потом огребли, плохо сработали
    3. В конце не успевали, лучше более подробно последнюю часть вместо
    первой
    4. В этот раз придётся рассказывать про rSocket т.к не стоит рассчитывать
    что перед нами будет доклад докуки
    5. Список докладов на посмотреть в конце( егорова + докуки)

    View Slide

  3. @tolkv
    @lavcraft
    @jekaborisov
    @jeka1978

    View Slide

  4. Spring
    Reactive
    Or Not Reactive

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

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

    View Slide

  17. View Slide

  18. Архитектура с RestTemplate и loop
    - Заканчиваются connection на tomcat → жопа
    - Про стартер. Как он используется. Какие эндпоинты добавляет
    - Какие настройки есть, как сервисы находят друг друга
    - Какие вещи нужно было бы добавить ещё в стартер чтобы он стал
    действительно полезным и универсальным
    - Понимаем что дописывать эти вещи сложно и мы хотим WebFlux
    Картиночка с котом с оторванным хвостом

    View Slide

  19. Архитектура с RestTemplate и loop

    View Slide

  20. Архитектура с RestTemplate и loop
    restTemplate.postForEntity(
    "http://localhost:8081/analyse/letter",
    Letter,
    Void.class
    );

    View Slide

  21. Архитектура с RestTemplate и loop
    Ещё N пожалуйста
    restTemplate.getForObject(
    adjustmentProperties.getUrl() + "/" + n,
    Void.class
    );

    View Slide

  22. Архитектура с RestTemplate и loop
    restTemplate.postForEntity(
    "http://localhost:8081/analyse/letter",
    Letter,
    Void.class
    );
    N

    View Slide

  23. Архитектура с RestTemplate и loop
    Ещё N пожалуйста
    Вот N писем
    Ещё N пожалуйста
    Вот N писем

    View Slide

  24. restTemplate.postForEntity(
    "http://localhost:8081/analyse/letter",
    Letter,
    Void.class
    );
    Смерть через RestTemplate
    restTemplate.getForObject(
    adjustmentProperties.getUrl() + "/" + n,
    id.class
    restTemplate.postForEntity(
    "http://localhost:8081/analyse/letter",
    Letter,
    Void.class
    );
    restTemplate.postForEntity(
    "http://localhost:8081/analyse/letter",
    Letter,
    Void.class
    );

    View Slide

  25. Какие у наc метрики
    Flux Flux Flux

    View Slide

  26. Tomcat не выдержал

    View Slide

  27. View Slide

  28. View Slide

  29. Тут не показываем табличку а показываем тома
    Понятно что тут ничего не работает, но потом будут интересные метрики
    - Разъяснить про табличку. Что в ней что значит и как её читать

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  35. View Slide

  36. View Slide

  37. ...

    View Slide

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

    View Slide

  39. 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

  40. 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

  41. 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

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

    View Slide

  43. Demo
    PoC

    View Slide

  44. 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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. Webflux

    View Slide

  61. Spring Webflux & Project Reactor

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  65. exchange
    WebClient
    subscribe
    Flux
    Controller
    http connection

    View Slide

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

    View Slide

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

    View Slide

  68. Demo
    Webflux

    View Slide

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

    View Slide

  70. 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

  71. 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

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

    View Slide

  73. subscribe( )
    Subscriber
    Flux
    reactor-netty
    LetterController

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  84. Demo
    Webflux #2

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  88. WebFlux
    Me

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  93. Похимичим
    с буферами?

    View Slide

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

    View Slide

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

    View Slide

  96. Demo
    Webflux #3

    View Slide

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

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

    View Slide

  99. network
    doOnNext
    Так где мои письма??
    FlatMapFlux
    request(8)
    c flatMap

    View Slide

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

    View Slide

  101. А что с нашими 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

  102. Netty OOM ?

    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. А знаете как это пофиксили?

    View Slide

  105. View Slide

  106. View Slide

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

    View Slide

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

    View Slide

  109. View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

  114. 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

  115. 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

  116. 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

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

    View Slide

  118. 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

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

    View Slide

  120. 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

  121. 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

  122. Java Nio
    Nio/Epoll/KQueue Event Loops
    KQueueEventLoop SelectorKey
    Ну такое..
    Буду писать на Spring-MVC

    View Slide

  123. А что с нашими 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

  124. View Slide

  125. Java Nio
    Nio/Epoll/KQueue Event Loops
    KQueueEventLoop SelectorKey
    Ну такое..
    Буду писать на Spring-MVC

    View Slide

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

    View Slide

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

    View Slide

  128. Хочу Backpressure

    View Slide

  129. Webflux

    View Slide

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

    View Slide

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

    View Slide

  132. Publisher
    Publisher/
    Subscriber
    Реактивные стримы
    Subscriber
    Next
    Complete
    Error

    View Slide

  133. Demo
    RSocket #4

    View Slide

  134. RSocket идеальное решение?
    В отличии от Webflux не так «разрекламирован»
    При этом встроен в Spring с версии 5.2

    View Slide

  135. Реактивные стримы
    connection
    connection

    View Slide

  136. Реактивные стримы
    connection1
    connection1
    connection2
    connection2

    View Slide

  137. Реактивные стримы
    connection1
    connection1
    connection2
    connection2
    connection3
    connection3

    View Slide

  138. RSocket в нашем
    примере не
    мультиплексирует
    соединения :(
    connection3
    connection1
    connection2

    View Slide

  139. RSocket в нашем
    примере не
    мультиплексирует
    соеднения :(
    connection1
    connection2
    Но это решаемо
    connection3

    View Slide

  140. connection1
    connection1
    connection2
    connection3
    Реактивные стримы с
    мультиплексированием
    Не зависит от количества
    соединений от к

    View Slide

  141. А как встроен?
    $ ./gradlew :pechkin-service:dI --dependency io.rsocket:rsocket-core
    io.rsocket:rsocket-core:1.0.0-RC5
    +--- org.springframework.boot:spring-boot-dependencies:2.2.0.RELEASE (*)
    \--- org.springframework.boot:spring-boot-starter-rsocket:2.2.0.RELEASE (*)

    View Slide

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

    View Slide

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

    View Slide

  144. Сияющие технологии
    1. В каких то задачах Webflux будет хорош
    a. Нет потребности в контроле Backpressure но внутри есть реактивная логика
    b. Быстрая интеграция с существующими API Reactor
    2. В каких то задачах RSocket будет плох
    3. Но своё решение всегда как минимум заставляет задуматься

    View Slide

  145. Hand Made Starter Webflux RSocket
    Максимальное
    использование
    ресурсов
    + + +
    Максимально
    быстро
    +- + +
    Backpressure - - +-
    Всё из коробки - +- +
    Просто
    использовать
    - - -

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

  150. Вопросы

    View Slide

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

    View Slide

  152. Ссылки
    Код с демо
    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
    Rsocket
    1. Rsocket.io
    2. Протокол RSocket – будущее. Доклад Олега Докуки

    View Slide