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.

Oleh Dokuka

May 21, 2018
Tweet

More Decks by Oleh Dokuka

Other Decks in Programming

Transcript

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

    View Slide

  2. 2

    View Slide

  3. 2

    View Slide

  4. 2

    View Slide

  5. 2

    View Slide

  6. 2

    View Slide

  7. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. А будет
    !7

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. Какие требования?
    !9

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. Нефункциональные
    !11

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. Какой сервер?
    !12

    View Slide

  29. 13

    View Slide

  30. 13

    View Slide

  31. 14

    View Slide

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

    View Slide

  33. 14

    View Slide

  34. !15

    View Slide

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

    View Slide

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

    View Slide

  37. !16

    View Slide

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

    View Slide

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

    View Slide

  40. 1 Netty ≈ 2 Tomcat
    !17

    View Slide

  41. !18

    View Slide

  42. !18

    View Slide

  43. !18

    View Slide

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

    View Slide

  45. Reactive Stack (WebFlux)
    @Controller, @RequestMapping,…
    Spring MVC
    Servlet API
    Servlet Container
    Default Stack (WebMVC)
    !20

    View Slide

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

    View Slide

  47. 21
    Project Reactor

    View Slide

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

    View Slide

  49. Reactive Types
    !23

    View Slide

  50. •Mono

    Reactive Types
    !23

    View Slide

  51. •Mono

    Reactive Types
    !23

    View Slide

  52. •Mono
    Reactive Types
    !24

    View Slide

  53. •Mono
    •Flux
    Reactive Types
    !24

    View Slide

  54. •Mono
    •Flux
    Reactive Types
    !24

    View Slide

  55. Reactive Types
    abstract class Flux
    implements Publisher {
    ...
    }

    View Slide

  56. Reactive Types
    abstract class Flux
    implements Publisher {
    ...
    }
    abstract class Flux
    implements Publisher {
    ...
    }

    View Slide

  57. Flux.just(1, 2, 3, 4)
    .map(…)
    .filter(…)
    .map(…)
    .doOnNext(…)
    .subscribe()

    View Slide

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

    View Slide

  59. То есть
    !27

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  68. Для WebSocket
    !30

    View Slide

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

    !30

    View Slide

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

    View Slide

  71. interface WebSocketHandler {
    Mono handle(WebSocketSession session);
    }
    interface WebSocketHandler {
    Mono handle(WebSocketSession session);
    }
    !31

    View Slide

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

    View Slide

  73. interface WebSocketSession {
    Flux receive();
    Mono send(Publisher messages);
    }
    !32

    View Slide

  74. interface WebSocketSession {
    Flux receive();
    Mono send(Publisher messages);
    }
    interface WebSocketSession {
    Flux receive();
    Mono send(Publisher messages);
    }
    !32

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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()
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  114. Что запомнить!?
    !48

    View Slide

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

    View Slide

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

    View Slide

  117. Помогает
    !49

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  122. Не очень
    !50

    View Slide

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

    View Slide

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

    View Slide

  125. Не очень
    •Конфигурировать WebSocket API
    •Конвертировать bytes java
    •Управлять ссылками
    !50

    View Slide

  126. Первые данные
    !51

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  133. interface WebSocketClient {
    Mono execute(URI url,
    WebSocketHandler handler);
    }
    interface WebSocketClient {
    Mono execute(URI url,
    WebSocketHandler handler);
    }
    !54

    View Slide

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

    View Slide

  135. Что создадим?
    !55

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  149. BitmexWebSocketConnection
    ClientWebSocketConnection
    !60

    View Slide

  150. !61
    BitmexWebSocketConnection
    ClientWebSocketConnection

    View Slide

  151. !61
    BitmexWebSocketConnection
    ClientWebSocketConnection

    View Slide

  152. !62
    BitmexWebSocketConnection
    ClientWebSocketConnection

    View Slide

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

    View Slide

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

    View Slide

  155. BitmexWebSocketConnection
    BitmexWebSocketConnection
    BitmexWebSocketConnection
    BitmexWebSocketConnection
    BitmexWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    !64

    View Slide

  156. BitmexWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    !64

    View Slide

  157. BitmexWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    ClientWebSocketConnection
    !64

    View Slide

  158. А как же Bitfinex?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  165. DirectProcessor
    .publish()
    ClientWebSocketConnection
    ClientWebSocketConnection
    !68

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  177. !71
    Не очень

    View Slide

  178. !71
    Не очень

    View Slide

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

    View Slide

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

    View Slide

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

    !73

    View Slide

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

    !74

    View Slide

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

    View Slide

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

    View Slide

  185. Что нужно?
    •MongoDB - реактивная база данных
    •Spring Data Mongo Reactive
    •Конфигурация SpringSecurity - для
    пользовательского доступа
    !77

    View Slide

  186. class ReactiveSecurityContextHolder {
    static Mono getContext()
    }
    !78

    View Slide

  187. class ReactiveSecurityContextHolder {
    static Mono getContext()
    }
    class ReactiveSecurityContextHolder {
    static Mono getContext()
    }
    !78

    View Slide

  188. Что создадим?
    !79

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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}

    View Slide

  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}

    View Slide

  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}

    View Slide

  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}

    View Slide

  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

    View Slide

  210. !84

    View Slide

  211. !84
    ReactorContextWebFilter

    View Slide

  212. !84
    ReactorContextWebFilter

    View Slide

  213. stateStreamSecurityProxy
    stateStream()
    !84
    ReactorContextWebFilter

    View Slide

  214. stateStreamSecurityProxy
    stateStream()
    !84
    ReactorContextWebFilter

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  232. Помогает
    !88

    View Slide

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

    View Slide

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

    View Slide

  235. !89
    Не очень

    View Slide

  236. !89
    Не очень

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  241. class Wallet {
    ...
    Wallet withdraw(float amount)
    Wallet adjust(float amount)
    }
    !94

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  245. interface WalletService {
    Flux> changesStream();
    Mono withdraw(Message trade);
    Mono adjust(Message trade);
    Mono rollback(Message trade);
    }
    !98

    View Slide

  246. Что создадим?
    !99

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  266. !106

    View Slide

  267. !106

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  279. !111

    View Slide

  280. !111

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  291. !116

    View Slide

  292. !116

    View Slide

  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

    View Slide

  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

    View Slide

  295. Есть БУГ

    View Slide

  296. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  301. Помогает
    !121

    View Slide

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

    View Slide

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

    View Slide

  304. Не очень
    !122

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  310. !125
    Talk is cheap. Show me the code.
    - Linus Torvalds

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  315. Помогает
    !127

    View Slide

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

    View Slide

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

    View Slide

  318. !128
    Не очень

    View Slide

  319. !128
    Не очень

    View Slide

  320. Итожим
    !129

    View Slide

  321. 130

    View Slide

  322. 130

    View Slide

  323. 130

    View Slide

  324. 130

    View Slide

  325. 130
    ?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  341. Где применять?
    !143

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  346. https://goo.gl/JZHhrq
    Код живет тут
    !145

    View Slide

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

    View Slide

  348. View Slide

  349. View Slide