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

How to build a crypto trading platform with Spring 5 and Reactor 3

How to build a crypto trading platform with Spring 5 and Reactor 3

In the talk, we will look at Reactive approaches with Spring 5 and Reactor 3 and see how to build a Reactive System using Spring Reactive Stack in details. In turn, we will discuss the common business needs, where these techniques fit well and how they may help you in solving complex problems in the most elegant and efficient way.

During the talk, we are going to build a Reactive Crypto-Trading Platform.

The central point which will be covered during the talk:

* Analyze of how to build a simple WebSocket API for data transfer;
* Analyze of existing Crypto-Platforms and the ways of integration with them using Reactor 3 and Spring WebFlux;
* Implementation of Trading and a Customer’s Wallet features;
* Security in Reactive solution with Reactive Spring Security;

What will be skipped:

* Blockchain mechanism;
* Purchasing/Sales mechanism of crypto-currencies in detail;
* Kotlin, obviously :);

Throughout all the talk, we will try to understand whether Reactor 3 and New Reactive Spring 5 helps us or not in solving of common business needs. Try to find out where Reactor 3 shines brightly, what works in WebFlux and what does not. Finally, we will be capable of making the decision whether we can already start working with Spring Boot 2 without fear.

B31d4d4bd89bff2bc668026b714dc8cf?s=128

Oleh Dokuka

May 21, 2018
Tweet

Transcript

  1. Строим крипто-трейдинг платформу Олег Докука

  2. 2

  3. 2

  4. 2

  5. 2

  6. 2

  7. None
  8. Чего НЕ будет? !4

  9. А не будет !5

  10. А не будет •Blockchain •Kotlin •Тестирования !5

  11. Что будет? !6

  12. А будет !7

  13. А будет •Как построить крипто-трейдинг платформу !7

  14. А будет •Как построить крипто-трейдинг платформу •Spring + !7

  15. А будет •Как построить крипто-трейдинг платформу •Spring + •Разбор подходов

    и особенностей с Reactor 3 !7
  16. А будет •Как построить крипто-трейдинг платформу •Spring + •Разбор подходов

    и особенностей с Reactor 3 !8
  17. Какие требования? !9

  18. Функциональные !10

  19. Функциональные •RealTime стоимость Битка !10

  20. Функциональные •RealTime стоимость Битка •Графики, лента продаж и прочее !10

  21. Функциональные •RealTime стоимость Битка •Графики, лента продаж и прочее •Пользовательский

    кошелек !10
  22. Функциональные •RealTime стоимость Битка •Графики, лента продаж и прочее •Пользовательский

    кошелек •Торговля биткоином !10
  23. Нефункциональные !11

  24. Нефункциональные •Высокая пропускная способность и быстрота ответа !11

  25. Нефункциональные •Высокая пропускная способность и быстрота ответа •Эффективная утилизация железа

    !11
  26. Нефункциональные •Высокая пропускная способность и быстрота ответа •Эффективная утилизация железа

    •Отказоустойчивость !11
  27. Нефункциональные •Высокая пропускная способность и быстрота ответа •Эффективная утилизация железа

    •Отказоустойчивость •Java + Spring Stack !11
  28. Какой сервер? !12

  29. 13

  30. 13

  31. 14

  32. 14 Асинхронный, event-driven фреймворк

  33. 14

  34. !15

  35. •Асинхронные, Не блокирующие операции !15

  36. •Асинхронные, Не блокирующие операции •Event-Loop !15

  37. !16

  38. •Высокая пропускная способность !16

  39. •Высокая пропускная способность •Эффективная утилизация ресурсов !16

  40. 1 Netty ≈ 2 Tomcat !17

  41. !18

  42. !18

  43. !18

  44. WebFlux WebMVC на реактивных стероидах !19

  45. Reactive Stack (WebFlux) @Controller, @RequestMapping,… Spring MVC Servlet API Servlet

    Container Default Stack (WebMVC) !20
  46. Reactive Stack (WebFlux) @Controller, @RequestMapping,… Spring WebFlux HTTP/Reactive-Streams Servlet 3.1,

    Netty, Undertow !20
  47. 21 Project Reactor

  48. 22 Flux.just(1, 2, 3, 4) .map(mapFunction()) .filter(filterFunction()) .map(mapFunction()) .doOnNext(someAction()) .subscribe()

  49. Reactive Types !23

  50. •Mono<T> • Reactive Types !23

  51. •Mono<T> • Reactive Types !23

  52. •Mono<T> Reactive Types !24

  53. •Mono<T> •Flux<T> Reactive Types !24

  54. •Mono<T> •Flux<T> Reactive Types !24

  55. Reactive Types abstract class Flux<T> implements Publisher<T> { ... }

  56. Reactive Types abstract class Flux<T> implements Publisher<T> { ... }

    abstract class Flux<T> implements Publisher<T> { ... }
  57. Flux.just(1, 2, 3, 4) .map(…) .filter(…) .map(…) .doOnNext(…) .subscribe()

  58. Flux.just(1, 2, 3, 4) .map(…) .filter(…) .map(…) .doOnNext(…) .subscribe() Операторы

  59. То есть !27

  60. То есть •Netty !27

  61. То есть •Netty •Spring WebFlux !27

  62. То есть •Netty •Spring WebFlux •Project Reactor 3 !27

  63. То есть •Netty •Spring WebFlux •Project Reactor 3 •Spring Boot

    2 !27
  64. Начнем с API !28

  65. Что нужно? !29

  66. Что нужно? •Вернуть HTML trade.io/ !29

  67. Что нужно? •Вернуть HTML trade.io/ •WebSocket trade.io/stream !29

  68. Для WebSocket !30

  69. Для WebSocket •Реализовать WebSocketHandler • !30

  70. interface WebSocketHandler { Mono<Void> handle(WebSocketSession session); } !31

  71. interface WebSocketHandler { Mono<Void> handle(WebSocketSession session); } interface WebSocketHandler {

    Mono<Void> handle(WebSocketSession session); } !31
  72. interface WebSocketHandler { Mono<Void> handle(WebSocketSession session); } interface WebSocketHandler {

    Mono<Void> handle(WebSocketSession session); } interface WebSocketHandler { Mono<Void> handle(WebSocketSession session); } !31
  73. interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } !32

  74. interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } interface

    WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } !32
  75. interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } interface

    WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } !32
  76. interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } interface

    WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } interface WebSocketSession { Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); } !32
  77. class WebSocketMessage { DataBuffer getPayload() String getPayloadAsText() WebSocketMessage retain() void

    release() } !33
  78. class WebSocketMessage { DataBuffer getPayload() String getPayloadAsText() WebSocketMessage retain() void

    release() } !33 class WebSocketMessage { DataBuffer getPayload() String getPayloadAsText() WebSocketMessage retain() void release() }
  79. class WebSocketMessage { DataBuffer getPayload() String getPayloadAsText() WebSocketMessage retain() void

    release() } !33 class WebSocketMessage { DataBuffer getPayload() String getPayloadAsText() WebSocketMessage retain() void release() } class WebSocketMessage { DataBuffer getPayload() String getPayloadAsText() WebSocketMessage retain() void release() }
  80. Для WebSocket •Реализовать WebSocketHandler !34

  81. Для WebSocket •Реализовать WebSocketHandler •Добавить конфигурацию !34

  82. Для WebSocket •Реализовать WebSocketHandler •Добавить конфигурацию •Протокол - JSON !35

  83. Что создадим? !36

  84. Что создадим? •IndexController.java !36

  85. Что создадим? •IndexController.java •CryptoChannel.java !36

  86. Что создадим? •IndexController.java •CryptoChannel.java •WebSocketConfiguration.java !36

  87. Что создадим? •IndexController.java •CryptoChannel.java •WebSocketConfiguration.java •Message.java !36

  88. Что создадим? •IndexController.java •CryptoChannel.java •WebSocketConfiguration.java •Message.java •WebSocketMessageMapper.java !36

  89. Что создадим? •IndexController.java •CryptoChannel.java •WebSocketConfiguration.java •Message.java •WebSocketMessageMapper.java !37

  90. !38 Talk is cheap. Show me the code. - Linus

    Torvalds
  91. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); !39
  92. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); !39
  93. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); !39
  94. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !39
  95. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !40
  96. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !40
  97. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !40
  98. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !41
  99. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !41
  100. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !41
  101. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !42
  102. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !42
  103. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !43
  104. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !43
  105. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !43
  106. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !44
  107. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !44
  108. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !45
  109. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !45
  110. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !46
  111. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !46
  112. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !47
  113. s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…))

    .map(db -> new WebSocketMessage(…)) .as(session::send); s.receive() .map(WebSocketMessage::retain) .map(WebSocketMessage::getPayload) .publishOn(Schedulers.parallel()) .transform(mapper::decode) .transform(this::doHandle) .onBackpressureBuffer() .transform(s -> mapper.encode(…)) .map(db -> new WebSocketMessage(…)) .as(session::send); .map(…) .publishOn(…) .onBackpressure() .tranform(…) .tranform(…) .map(…) !47
  114. Что запомнить!? !48

  115. Что запомнить!? •WebSocketMessage.retain/ release для подсчёта ссылок !48

  116. Что запомнить!? •WebSocketMessage.retain/ release для подсчёта ссылок •.publishOn() для переноса

    работу с Event-Loop !48
  117. Помогает !49

  118. Помогает •Работать с Netty !49

  119. Помогает •Работать с Netty •Построить чистый асинхронный код !49

  120. Помогает •Работать с Netty •Построить чистый асинхронный код •Управлять потоками

    !49
  121. Помогает •Работать с Netty •Построить чистый асинхронный код •Управлять потоками

    •Управлять Backpressure !49
  122. Не очень !50

  123. Не очень •Конфигурировать WebSocket API !50

  124. Не очень •Конфигурировать WebSocket API •Конвертировать bytes <-> java !50

  125. Не очень •Конфигурировать WebSocket API •Конвертировать bytes <-> java •Управлять

    ссылками !50
  126. Первые данные !51

  127. Откуда брать? !52

  128. Откуда брать? •Bitmex - WebSocket API !52

  129. Откуда брать? •Bitmex - WebSocket API •Bitfinex - WebSocket API

    !52
  130. Что нужно? !53

  131. Что нужно? •WebSocket Client !53

  132. interface WebSocketClient { Mono<Void> execute(URI url, WebSocketHandler handler); } !54

  133. interface WebSocketClient { Mono<Void> execute(URI url, WebSocketHandler handler); } interface

    WebSocketClient { Mono<Void> execute(URI url, WebSocketHandler handler); } !54
  134. interface WebSocketClient { Mono<Void> execute(URI url, WebSocketHandler handler); } interface

    WebSocketClient { Mono<Void> execute(URI url, WebSocketHandler handler); } !54 interface WebSocketClient { Mono<Void> execute(URI url, WebSocketHandler handler); }
  135. Что создадим? !55

  136. Что создадим? •CryptoService.java !55

  137. Что создадим? •CryptoService.java •BitmexMessage.java !55

  138. Что создадим? •CryptoService.java •BitmexMessage.java •BitmexMessageMapper.java !55

  139. Что создадим? •CryptoService.java •BitmexMessage.java •BitmexMessageMapper.java •BitmexService.java !55

  140. Что создадим? •CryptoService.java •BitmexMessage.java •BitmexMessageMapper.java •BitmexService.java !56

  141. !57 Talk is cheap. Show me the code. - Linus

    Torvalds
  142. map(…) Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…)

    .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); publishOn(…) then() flatMapIterable(…) !58
  143. map(…) Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…)

    .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); publishOn(…) then() flatMapIterable(…) Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); !58
  144. map(…) Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…)

    .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); publishOn(…) then() flatMapIterable(…) Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); !58
  145. Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…)

    .doOnNext(sink::next) .then(); ... ); map(…) publishOn(…) then() flatMapIterable(…) !59
  146. Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…)

    .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); doOnNext(sink::next) map(…) publishOn(…) then() flatMapIterable(…) !59
  147. Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…)

    .doOnNext(sink::next) .then(); ... ); Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…) .doOnNext(sink::next) .then(); ... ); doOnNext(sink::next) map(…) publishOn(…) then() flatMapIterable(…) !59
  148. Flux.create(sink -> ... s -> s.receive() .skip(6) .map(WSM::getPayloadAsText) .publishOn(…) .flatMapIterable(…)

    .doOnNext(sink::next) .then(); ... ); doOnNext(sink::next) map(…) publishOn(…) then() flatMapIterable(…) !60
  149. BitmexWebSocketConnection ClientWebSocketConnection !60

  150. !61 BitmexWebSocketConnection ClientWebSocketConnection

  151. !61 BitmexWebSocketConnection ClientWebSocketConnection

  152. !62 BitmexWebSocketConnection ClientWebSocketConnection

  153. BitmexWebSocketConnection ClientWebSocketConnection BitmexWebSocketConnection ClientWebSocketConnection BitmexWebSocketConnection ClientWebSocketConnection BitmexWebSocketConnection ClientWebSocketConnection !62 BitmexWebSocketConnection

    ClientWebSocketConnection
  154. !63 Talk is cheap. Show me the code. - Linus

    Torvalds
  155. BitmexWebSocketConnection BitmexWebSocketConnection BitmexWebSocketConnection BitmexWebSocketConnection BitmexWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection

    !64
  156. BitmexWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !64

  157. BitmexWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !64

  158. А как же Bitfinex?

  159. !66 Talk is cheap. Show me the code. - Linus

    Torvalds
  160. .publish() DirectProcessor ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !67

  161. .publish() DirectProcessor ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !67

  162. .publish() DirectProcessor ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !67

  163. DirectProcessor .publish() ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !68

  164. DirectProcessor .publish() ClientWebSocketConnection ClientWebSocketConnection ClientWebSocketConnection !68

  165. DirectProcessor .publish() ClientWebSocketConnection ClientWebSocketConnection !68

  166. Что запомнить!? !69

  167. Что запомнить!? •FluxSink для перенаправления !69

  168. Что запомнить!? •FluxSink для перенаправления •.publish() для уравниловки !69

  169. Что запомнить!? •FluxSink для перенаправления •.publish() для уравниловки !69

  170. Что запомнить!? •FluxSink для перенаправления •.publish() для уравниловки •DirectProcessor для

    push !69
  171. Что запомнить!? •FluxSink для перенаправления •.publish() для уравниловки •DirectProcessor для

    push !69
  172. Помогает !70

  173. Помогает •Работать с WebSocket !70

  174. Помогает •Работать с WebSocket •Перенаправлять данные !70

  175. Помогает •Работать с WebSocket •Перенаправлять данные •Мультикастить данные !70

  176. Помогает •Работать с WebSocket •Перенаправлять данные •Мультикастить данные •Объединять данные

    !70
  177. !71 Не очень

  178. !71 Не очень

  179. Строим Кошелёк !72

  180. Что нужно? !73

  181. Что нужно? •MongoDB - реактивная база данных • !73

  182. Что нужно? •MongoDB - реактивная база данных •Spring Data Mongo

    Reactive • !74
  183. interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> <S extends T> Mono<S>

    save(S entity); <S extends T> Flux<S> saveAll(Iterable<S> entities); <S extends T> Flux<S> saveAll(Publisher<S> entityStream); Mono<T> findById(ID id); Mono<T> findById(Mono<ID> id); Mono<Boolean> existsById(ID id); Mono<Boolean> existsById(Mono<ID> id); Flux<T> findAll(); Flux<T> findAllById(Iterable<ID> ids); Flux<T> findAllById(Publisher<ID> idStream); Mono<Long> count(); Mono<Void> deleteById(ID id); Mono<Void> delete(T entity); Mono<Void> deleteAll(Iterable<? extends T> entities); Mono<Void> deleteAll(Publisher<? extends T> entityStream); Mono<Void> deleteAll(); } 75
  184. interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> <S extends T> Mono<S>

    save(S entity); <S extends T> Flux<S> saveAll(Iterable<S> entities); <S extends T> Flux<S> saveAll(Publisher<S> entityStream); Mono<T> findById(ID id); Mono<T> findById(Mono<ID> id); Mono<Boolean> existsById(ID id); Mono<Boolean> existsById(Mono<ID> id); Flux<T> findAll(); Flux<T> findAllById(Iterable<ID> ids); Flux<T> findAllById(Publisher<ID> idStream); Mono<Long> count(); Mono<Void> deleteById(ID id); Mono<Void> delete(T entity); Mono<Void> deleteAll(Iterable<? extends T> entities); Mono<Void> deleteAll(Publisher<? extends T> entityStream); Mono<Void> deleteAll(); } 76
  185. Что нужно? •MongoDB - реактивная база данных •Spring Data Mongo

    Reactive •Конфигурация SpringSecurity - для пользовательского доступа !77
  186. class ReactiveSecurityContextHolder { static Mono<SecurityContext> getContext() } !78

  187. class ReactiveSecurityContextHolder { static Mono<SecurityContext> getContext() } class ReactiveSecurityContextHolder {

    static Mono<SecurityContext> getContext() } !78
  188. Что создадим? !79

  189. Что создадим? •Wallet.java !79

  190. Что создадим? •Wallet.java •WalletRepository.java !79

  191. Что создадим? •Wallet.java •WalletRepository.java •WalletService.java !79

  192. Что создадим? •Wallet.java •WalletRepository.java •WalletService.java •LocalWalletService.java !79

  193. Что создадим? •Wallet.java •WalletRepository.java •WalletService.java •LocalWalletService.java •LocalMessageMapper.java !79

  194. Что создадим? •Wallet.java •WalletRepository.java •WalletService.java •LocalWalletService.java •LocalMessageMapper.java !80

  195. !81 Talk is cheap. Show me the code. - Linus

    Torvalds
  196. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); !82

  197. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println) !82

  198. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println) null

    !82
  199. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println) !82

    {emptyMap}
  200. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println) !82

    {emptyMap} ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println)
  201. null ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscribe(out::println)

    !82
  202. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); !83

  203. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) !83

  204. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) ReactiveSecurityContextHolder

    .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) !83
  205. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) ReactiveSecurityContextHolder

    .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) !83 {emptyMap}
  206. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) ReactiveSecurityContextHolder

    .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) !83 {emptyMap}
  207. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) ReactiveSecurityContextHolder

    .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) !83 {security}
  208. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) ReactiveSecurityContextHolder

    .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) !83 {security}
  209. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) ReactiveSecurityContextHolder

    .getContext() .map(getAuthentication) .map(getName) .subscriberContext(security) .subscribe(out::println) Admin !83
  210. !84

  211. !84 ReactorContextWebFilter

  212. !84 ReactorContextWebFilter

  213. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter

  214. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter

  215. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter subscriberContext()

  216. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter subscriberContext()

  217. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter subscriberContext()

  218. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter subscriberContext()

  219. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter subscriberContext()

  220. stateStreamSecurityProxy stateStream() !84 ReactorContextWebFilter subscriberContext()

  221. stateStreamSecurityProxy stateStream() !85 ReactorContextWebFilter subscriberContext()

  222. stateStreamSecurityProxy stateStream() !85 ReactorContextWebFilter subscriberContext()

  223. stateStreamSecurityProxy stateStream() !85 ReactorContextWebFilter subscriberContext()

  224. stateStreamSecurityProxy stateStream() !85 ReactorContextWebFilter subscriberContext()

  225. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName);

  226. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ... ReactorContextWebFilter…

  227. ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); admin ReactiveSecurityContextHolder .getContext() .map(getAuthentication) .map(getName); ...

    ReactorContextWebFilter…
  228. Что запомнить!? !87

  229. Что запомнить!? •Замыкаем что бы получить Context !87

  230. Что запомнить!? •Замыкаем что бы получить Context •Context задом наперед

    !87
  231. Что запомнить!? •Замыкаем что бы получить Context •Context задом наперед

    •SecurityContext живет в Context !87
  232. Помогает !88

  233. Помогает •Работать реактивно с MongoDB !88

  234. Помогает •Работать реактивно с MongoDB •Защищать API просто !88

  235. !89 Не очень

  236. !89 Не очень

  237. Механика торговли !90

  238. Что нужно? •Сохранять результаты торгов в базу !91

  239. Бизнес механизм •Снять деньги с кошелька •Провести торги •В случае

    успеха - добавить деньги в кошелек •В случае неудачных торгов - вернуть деньги •В случае нехватки средств - ничего не делать !92
  240. Что измениться? •Wallet.java •CryptoService.java •WalletService.java !93

  241. class Wallet { ... Wallet withdraw(float amount) Wallet adjust(float amount)

    } !94
  242. Что измениться? •Wallet.java •CryptoService.java •WalletService.java !95

  243. interface CryptoService { Flux<Message<?>> stream(); default Mono<Void> trade(…) } !96

  244. Что измениться? •Wallet.java •CryptoService.java •WalletService.java !97

  245. interface WalletService { Flux<Message<Float>> changesStream(); Mono<Void> withdraw(Message<Message.Trade> trade); Mono<Void> adjust(Message<Message.Trade>

    trade); Mono<Void> rollback(Message<Message.Trade> trade); } !98
  246. Что создадим? !99

  247. Что создадим? •Trade.java !99

  248. Что создадим? •Trade.java •TradeRepository.java !99

  249. Что создадим? •Trade.java •TradeRepository.java •LocalCryptoService.java !99

  250. Что создадим? •Trade.java •TradeRepository.java •LocalCryptoService.java !100

  251. !101 Talk is cheap. Show me the code. - Linus

    Torvalds
  252. tradeOffer .onBackpressureBuffer() .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class,

    t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) ) .map(LocalMessageMapper::tradeToMessage) .doOnNext(stream.sink()::next) .then(); !102
  253. tradeOffer .onBackpressureBuffer() .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class,

    t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) ) .map(LocalMessageMapper::tradeToMessage) .doOnNext(stream.sink()::next) .then(); !102
  254. tradeOffer .onBackpressureBuffer() .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class,

    t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) ) .map(LocalMessageMapper::tradeToMessage) .doOnNext(stream.sink()::next) .then(); !102
  255. tradeOffer .onBackpressureBuffer() .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class,

    t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) ) .map(LocalMessageMapper::tradeToMessage) .doOnNext(stream.sink()::next) .then();!102
  256. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } !103
  257. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } !103
  258. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !103
  259. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !104
  260. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !104
  261. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !104
  262. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !104
  263. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !105
  264. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !105
  265. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !105
  266. !106

  267. !106

  268. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !107
  269. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !107
  270. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !107
  271. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !108
  272. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !108
  273. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !108
  274. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } !109
  275. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } !109
  276. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !109
  277. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !110
  278. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !110
  279. !111

  280. !111

  281. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(NEME) onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !112
  282. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(NEME) onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !112
  283. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } !113
  284. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } !113
  285. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !113
  286. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !114
  287. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !114
  288. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !114
  289. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !115
  290. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(rollback()) onErrorResume(NEME) adjust() doTrade() .withdraw() doStoreTrade() !115
  291. !116

  292. !116

  293. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(NEME) onErrorResume(rollback()) adjust() doTrade() .withdraw() doStoreTrade() onErrorResume(rollback()) !117
  294. .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t ->

    Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } .flatMap(trade -> ws.withdraw(trade) .then(doTrade(trade)) .then(ws.adjust(trade)) .then(doStoreTrade(trade)) .onErrorResume( NotEnoughMoneyException.class, t -> Mono.empty() ) .onErrorResume(t -> ws.rollback(trade) .then(Mono.empty()) ) } onErrorResume(NEME) onErrorResume(rollback()) adjust() doTrade() .withdraw() doStoreTrade() onErrorResume(rollback()) !117
  295. Есть БУГ

  296. None
  297. Что запомнить!? !120

  298. Что запомнить!? •.onErrorXXX для ошибок !120

  299. Что запомнить!? •.onErrorXXX для ошибок •.then для последовательности операций !120

  300. Что запомнить!? •.onErrorXXX для ошибок •.then для последовательности операций •Request/Session

    scope - своими руками !120
  301. Помогает !121

  302. Помогает •Работать с ошибками !121

  303. Помогает •Работать с ошибками •Строить сложное - просто !121

  304. Не очень !122

  305. Не очень •Конфигурировать Request/ Session scope !122

  306. А как же отказоустойчивость? !123

  307. Что нужно? !124

  308. Что нужно? •Стабильно работать в случае отказа внешних сервисов !124

  309. Что нужно? •Стабильно работать в случае отказа внешних сервисов •Стабильно

    работать в случае не авторизованного доступа !124
  310. !125 Talk is cheap. Show me the code. - Linus

    Torvalds
  311. Что запомнить!? !126

  312. Что запомнить!? •Изолируем отдельные компоненты !126

  313. Что запомнить!? •Изолируем отдельные компоненты •.timeout - чтобы долго не

    ждать !126
  314. Что запомнить!? •Изолируем отдельные компоненты •.timeout - чтобы долго не

    ждать •.retryWhen - для повторных попыток !126
  315. Помогает !127

  316. Помогает •Изолировать компоненты !127

  317. Помогает •Изолировать компоненты •Работать с переподключением !127

  318. !128 Не очень

  319. !128 Не очень

  320. Итожим !129

  321. 130

  322. 130

  323. 130

  324. 130

  325. 130 ?

  326. С чем уйти? !131

  327. WebFlux - тот же WebMVC но работает с Netty !132

  328. WebFlux + Reactor Чистый асинхронный код !133

  329. Ивентам - Event-Loop Бизнес логике - отдельный Thread Pool !134

  330. Экономь сокеты - .publish() данные !135

  331. Реактивная интеграция с Базой с - Spring Data Reactive !136

  332. Замыкаем потоки для общего Context !137

  333. Грустим (пока что) изза Request/Session Scopes !138

  334. Отлавливаем ошибки с - .onErrorXXX !139

  335. Остаемся непоколебимыми с - .retryWhen !140

  336. Экономим время с - .timeout !141

  337. Что еще упустили? !142

  338. Что еще упустили? •Дебажить сложно - но просто тестить с

    StepVerifier !142
  339. Что еще упустили? •Дебажить сложно - но просто тестить с

    StepVerifier •Нет реактивного JDBC (пока что) !142
  340. Что еще упустили? •Дебажить сложно - но просто тестить с

    StepVerifier •Нет реактивного JDBC (пока что) •Императивный код никто не отменял !142
  341. Где применять? !143

  342. Где применять? •Системы с высокой нагрузкой !143

  343. Где применять? •Системы с высокой нагрузкой •Там где нужно экономить

    на железе !143
  344. Где применять? •Системы с высокой нагрузкой •Там где нужно экономить

    на железе •Microservices orchestration !143
  345. На посмотреть •WebFlux воркшоп - https://bclozel.github.io/webflux- workshop/ •Играем с Reactor

    3 - https://tech.io/playgrounds/929/ reactive-programming-with-reactor-3/Intro •JDBC? Смотреть тут - https://www.youtube.com/watch? v=OiXu05WU7zI •Все еще не уверены? - Netflix вещает - https://goo.gl/ dMBUC7 !144
  346. https://goo.gl/JZHhrq Код живет тут !145

  347. Олег Докука /oleh.dokuka /OlegDokuka /OlehDokuka

  348. None
  349. None