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

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

40951719c6ca509831d5c38b764661c9?s=47 Kirill Tolkachev
April 05, 2019
500

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

40951719c6ca509831d5c38b764661c9?s=128

Kirill Tolkachev

April 05, 2019
Tweet

Transcript

  1. Spring Reactive Or Not Reactive … 1984

  2. @tolkv @lavcraft @jekaborisov @jeka1978

  3. Spring Reactive Or Not Reactive

  4. Задача

  5. Задача

  6. Задача

  7. Задача

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

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

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

    распознание Правоохранительные органы, принимают решение об аресте Расшифровщик писем, Выясняет чьё письмо о отсылает в Правоохранительные органы
  16. None
  17. Demo Наивная имплементация #0

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

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

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

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

    Buffer #P0 Passive Consumer Buffer #C2 Buffer #T1 Reject
  22. Demo Наивная имплементация #1

  23. None
  24. None
  25. ...

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

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

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

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

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

  31. Demo Manual backpressure

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  49. Spring Webflux & Project Reactor

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

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

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

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

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

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

  56. Demo Webflux

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

    body is missing
  58. 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
  59. 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 Для начала вычитывания
  60. Flux пайплайн жизненный цикл 1. Assembly Как new Builder().prop().build() Только

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    request(8) Ограничили рост буфера
  80. Чувак, почему так медленно? 1000rps vs 2rps ... doOnNext doOnNext(decode)

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

  82. Demo Webflux #3

  83. 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
  84. network doOnNext Чувак, где мои письма Flux request(unbounded) Drop? BP

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

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

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

    at j.n.Bits.reserveMemory(Bits.java:175) at j.n.DirectByteBuffer.<init>(DirectByteBuffer.java:118)
  88. Что произошло и как чинить?

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

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

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

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

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

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

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

    reactor-nio-1 letter-N Пулы DirectBuffer`ов netty socket 1. Копирует данные из cистемного сокета в [1ms] DirectBuffer [1000ms]
  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] работает независимо
  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] работает независимо
  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. У нас же есть Backpressure из коробки!

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

  106. Хочу Backpressure

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

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

  109. Demo Webflux #3

  110. RSocket идеальное решение? Можно было бы его продать так же

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

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

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

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

    2. В каких то задачах RSocket будет плох 3. Но своё решение всегда как минимум заставляет задуматься
  115. Выводы 1. Не всё то Reactive что с интегрировано с

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

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

  119. Вопросы

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

  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