Spring Boot Reactive Ripper Joker 2019

Spring Boot Reactive Ripper Joker 2019

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

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

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

40951719c6ca509831d5c38b764661c9?s=128

Kirill Tolkachev

October 24, 2019
Tweet

Transcript

  1. Spring Reactive Or Not Reactive … 1984

  2. Улучшения 1. Начало перенести в слайды и дропнуть демку ради

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

  4. Spring Reactive Or Not Reactive

  5. Задача

  6. Задача

  7. Задача

  8. Задача

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

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

  13. None
  14. None
  15. None
  16. Архитектура проекта «Big Brother» Перехватчик писем, отсылает все письма на

    распознание Правоохранительные органы, принимают решение об аресте Расшифровщик писем, Выясняет чьё письмо о отсылает в Правоохранительные органы
  17. None
  18. Архитектура с RestTemplate и loop - Заканчиваются connection на tomcat

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

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

  21. Архитектура с RestTemplate и loop Ещё N пожалуйста restTemplate.getForObject( adjustmentProperties.getUrl()

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

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

    писем Ещё N пожалуйста Вот N писем
  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 );
  25. Какие у наc метрики Flux<Status> Flux<Status> Flux<Status>

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

  27. None
  28. None
  29. Тут не показываем табличку а показываем тома Понятно что тут

    ничего не работает, но потом будут интересные метрики - Разъяснить про табличку. Что в ней что значит и как её читать
  30. Demo Наивная имплементация #0

  31. Push and Pull semantics related to consumer events Pusher Buffer

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

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

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

    Buffer #P0 Passive Consumer Buffer #C2 Buffer #T1 Reject
  35. None
  36. None
  37. ...

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

  39. pechkin-service big-brother-service agent-smith-service request(n) request(n) Buffer #P0 Buffer #P1 Buffer

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

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

    #P2 1t 8t 2t ∞ 8rps 2rps
  42. Задача Оптимизировать количество запросов под ресурсы в цепочке «Backpressure»

  43. Demo PoC

  44. pechkin-service big-brother-service agent-smith-service request(n) request(n) Buffer #P0 Buffer #P1 Buffer

    #P2 1t 8t 2t ∞ 8rps 2rps
  45. Варианты получать данные

  46. Что должен делать producer когда consumer не справляется Продюсер читает

    данные с нужной скоростью Нет проблемы В продюсер текут данные Всё что не успеваем отослать, суем в очередь, когда нет места в очереди, дропаем, то что менее важно
  47. Producer Middlewares events Buffer #P0 events Consumer #0 Consumer #1

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

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

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

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

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

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

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

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

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

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

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

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

    Push Service Passive Consumer Buffer #C2 Black Hole Buffer #P0
  60. Webflux

  61. Spring Webflux & Project Reactor

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

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

  64. WebClient is a new RestTemplate Было RestTemplateBuilder → RestTemplate RestTemplate

    — передаём статический текс в body Стало WebClient.Builder → WebClient WebClient — можем передать последовательность Flux в качестве body
  65. exchange WebClient subscribe Flux Controller http connection

  66. exchange WebClient reactor-netty jetty-client Java 11 http client Issue 19658

    subscribe Issue 21014 Flux closed open Controller http connection
  67. Letter generator Flux.<Letter>generate( sink -> sink.next(randomLetter()) );

  68. Demo Webflux

  69. WebFlux #1 public void processLetter(@RequestBody Flux<Letter> f) 400 BAD_REQUEST "Request

    body is missing
  70. void processLetter(Flux<Letter> f) f.doOnNext() .doOnNext() .log() .subscribe() return Mono.empty() /

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

    nothing / anything LetterController Close connection 400 BAD_REQUEST "Request body is missing Для начала вычитывания
  72. Flux пайплайн жизненный цикл 1. Assembly Как new Builder().prop().build() Только

    Flux.create().subscribe(subscriber) 2. В subscriber.onSubscribe() передаётся подписка – subscription 3. Через subscription выполняется первый запрос на ивенты или отписка
  73. subscribe( ) Subscriber Flux reactor-netty LetterController

  74. Flux .subscribe( ) Subscriber subscription.request(N ) → onSubscribe(subscription) onNext( )

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

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

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

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

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

    ) .map( ) .filter( ) onError( ) reactor-netty LetterController
  80. WebFlux #1 public Mono<Void> processLetter(@RequestBody Flux<Letter> f) Mono.empty() Mono.never()

  81. Mon<Void> processLetter(Flux<Letter> f) f.doOnNext() .doOnNext() .log() .subscribe() return Mono.never() LetterController

    NO Close connection reactor-netty
  82. Немного о HTTP request GET / HTTP 1/1 HOST: dfsdfg

    \n\r response
  83. Mon<Void> processLetter(Flux<Letter> f) return f.doOnNext() .doOnNext() .log() .then() LetterController Close

    connection Mono.onComplete() reactor-netty
  84. Demo Webflux #2

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

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

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

  88. WebFlux Me

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

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

    Flux request(unbounded)
  91. Нас засудят за сексизм!

  92. network doOnNext Чувак/чувиха, где мои письма – в буферах! FlatMapFlux

    request(8) Ограничили рост буфера
  93. Похимичим с буферами?

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

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

  96. Demo Webflux #3

  97. Mon<Void> processLetter(Flux<Letter> f) return f.doOnNext() .flatMap( letter -> Mono.fromCallable(() ->

    decode(letter)) .subscribeOn(fromExecutor(threadPool)), threadPool.getMaximumPoolSize()) .log() .then() LetterController reactor-netty
  98. network doOnNext Чувак, где мои письма Flux request(unbounded) Drop? BP

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

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

  101. А что с нашими DirectBuffer`ами? netty java.lang.OutOfMemoryError: Direct buffer memory

    at j.n.Bits.reserveMemory(Bits.java:175) at j.n.DirectByteBuffer.<init>(DirectByteBuffer.java:118)
  102. Netty OOM ?

  103. А что с нашими DirectBuffer`ами? netty java.lang.OutOfMemoryError: Direct buffer memory

    at j.n.Bits.reserveMemory(Bits.java:175) at j.n.DirectByteBuffer.<init>(DirectByteBuffer.java:118)
  104. А знаете как это пофиксили?

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

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

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

  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
  112. TCP BP Без flatMap #0 flux netty netty flux reactor-netty

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

    reactor-nio-1 Пулы DirectBuffer`ов netty socket 1. Копирует данные из cистемного сокета в DirectBuffer
  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
  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
  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. ... и так повторяется
  117. TCP BP После добавления flatMap flux netty netty flux reactor-netty

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

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

    reactor-nio-1 letter-N Пулы DirectBuffer`ов netty socket 1. Копирует данные из cистемного сокета в [1ms] DirectBuffer [1000ms]
  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] работает независимо
  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] работает независимо
  122. Java Nio Nio/Epoll/KQueue Event Loops KQueueEventLoop SelectorKey Ну такое.. Буду

    писать на Spring-MVC
  123. А что с нашими DirectBuffer`ами? netty java.lang.OutOfMemoryError: Direct buffer memory

    at j.n.Bits.reserveMemory(Bits.java:175) at j.n.DirectByteBuffer.<init>(DirectByteBuffer.java:118)
  124. None
  125. Java Nio Nio/Epoll/KQueue Event Loops KQueueEventLoop SelectorKey Ну такое.. Буду

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

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

  128. Хочу Backpressure

  129. Webflux

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

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

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

  133. Demo RSocket #4

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

    При этом встроен в Spring с версии 5.2
  135. Реактивные стримы connection connection

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

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

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

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

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

    от количества соединений от к
  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 (*)
  142. Выводы ложные 1. К чёрту велосипеды 2. К чёрту webflux

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

    3. Да здравствует RSocket
  144. Сияющие технологии 1. В каких то задачах Webflux будет хорош

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

    + Максимально быстро +- + + Backpressure - - +- Всё из коробки - +- + Просто использовать - - -
  146. Выводы 1. Не всё то Reactive что с интегрировано с

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

    спикера 2. компании 3. Тренда Webflux хорош когда нужно сэкономить коннекшены и отправить пачку данных RSocket хорош во всех наших кейсах Свой велосипед всегда понятнее
  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
  149. Выводы – кривая обучения reactive effort/time Learn about reactive mastery

  150. Вопросы

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

  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 – будущее. Доклад Олега Докуки