Get Reactive With Project Reactor and Spring 5

Get Reactive With Project Reactor and Spring 5

Spring – the tool that is over 13 years pleases our hearts and eyes of customers. Despite this long history of development the project continues to follow the latest trends and provides to developers the awesome set of add-ons that simplify the daily life.

During this tech-talk, I would like to discuss delicacies which were cooked by developers from Pivotal and how Reactivity reflected on the 5th iteration of the framework.

B31d4d4bd89bff2bc668026b714dc8cf?s=128

Oleh Dokuka

May 26, 2017
Tweet

Transcript

  1. Get Reactive With Spring 5 And Project Reactor by Oleh

    Dokuka
  2. • JSE at Levi9 • 6+ dev experience • Spring

    fan • Reactivity preacher /oleh.dokuka /OlegDokuka /OlehDokuka WHO AM I?
  3. Agenda 1. Why Reactive? 2. Reactivity Generations 3. Reactive Streams

    4. Project Reactor 5. Reactivity in Action (Project Reactor + Spring 5)
  4. So, why Reactive?1

  5. Requirements of Nowadays https://sites.psu.edu/luopassion2/2016/01/25/a-brief-history-of-the-internet-of-things-iot/

  6. Lean resource management https://i.ytimg.com/vi/EiH1sJCDs9c/maxresdefault.jpg

  7. Asynchrony http://www.cntraveler.com/story/the-main-reasons-your-passport-application-will-be-denied

  8. Fault Tolerance

  9. Powerful way of thinking http://www.wallpaperup.com/200710/pipes_cranes.html

  10. Reactive-Manifesto2

  11. Sounds Cool

  12. Reactivity Generations 3

  13. Handler Observer Java

  14. Skill Knowledge Rx.NET

  15. Akka RxJava

  16. RxJava AkkaStreams

  17. RxJava 2 AkkaStream Project Reactor Reactive Streams Specified

  18. None
  19. Reactive Streams4

  20. What is the Purpose?

  21. Backpressure

  22. None
  23. Common API

  24. Publisher Subscriber

  25. Publisher Subscriber public interface Publisher<T> { public void subscribe( Subscriber<?

    super T> s ); }
  26. Publisher Subscriber public interface Subscriber<T> { public void onSubscribe(Subscription s);

    public void onNext(T t); public void onError(Throwable t); public void onComplete(); }
  27. Publisher Subscriber Subscription

  28. Publisher Subscriber Subscription public interface Subscription { public void request(long

    n); public void cancel(); }
  29. Publisher Subscriber Subscription public interface Subscription { public void request(long

    n); public void cancel(); }
  30. Publisher Subscriber Subscription

  31. Processor Publisher Subscriber Subscription

  32. Publisher Subscriber Subscription Processor public interface Processor<T, R> extends Subscriber<T>,

    Publisher<R> {}
  33. Example

  34. None
  35. None
  36. None
  37. None
  38. None
  39. None
  40. None
  41. None
  42. None
  43. Project Reactor

  44. by

  45. RxJava-like API

  46. Reactive Types

  47. Flux

  48. like Stream<T>

  49. None
  50. Mono

  51. like CompletableFuture<T>

  52. None
  53. Useful Operators

  54. Operator == Very Smart Publisher

  55. merge .merge(…)

  56. zip .zip(…)

  57. zip .zip(…)

  58. publish .publish(…)

  59. .materialize(…) materialize

  60. materialize .materialize(…)

  61. Thread A Thread Main .publishOn(…) publishOn

  62. None
  63. RxJava.observeOn == Reactor.publishOn

  64. Remember!

  65. Nothing happens until .subscribe ()

  66. Reactivity in Action

  67. What we`re going to do?

  68. Refactoring

  69. What do we have?

  70. Chat Application

  71. Functionality • Gitter Chat Communication • Messaging Statistic

  72. Demo

  73. None
  74. Toolkit • Spring Framework 4 • Spring Boot • Spring

    MVC • Spring Data JPA
  75. Architecture

  76. User Client Gitter Service Whole Application DB Layer Service Layer

    Controllers Layer
  77. Controllers Layer

  78. Returns Thymeleaf View

  79. REST Resources

  80. Services Layer

  81. External Chats SPI

  82. Domain Services

  83. Gitter integration

  84. Implementation

  85. Persistence Layer

  86. Database Model

  87. How it works?

  88. None
  89. None
  90. None
  91. None
  92. Problems

  93. Problems • Pulling model

  94. None
  95. Problems • Pulling model • Too much blocking I/O

  96. Solutions

  97. Apply Reactive Manifesto

  98. None
  99. Async Messaging Push Model

  100. Apply Streaming API

  101. None
  102. None
  103. Async Messaging Elasticity

  104. Dedicate ThreadPool

  105. None
  106. None
  107. Async Messaging Push Model

  108. Apply WebSocket

  109. None
  110. How to bring it all together?

  111. Spring 5

  112. Painless replacement

  113. WebFlux @Controller, @RequestMapping,… Spring MVC Servlet API Servlet Container Spring

    Web Reactive Reactive HTTP Servlet, Netty, Undertow
  114. None
  115. None
  116. @RestController @RequestMapping("/api/v1/statistics") public class StatisticResource { private final StatisticService statisticService;

    @Autowired public StatisticResource( StatisticService statisticService) { this.statisticService = statisticService; } @GetMapping("/users") public List<UsersStatisticVM> getUsersStatistic() { return statisticService.getUsersStatistic(); } }
  117. @RestController @RequestMapping("/api/v1/statistics") public class StatisticResource { private final StatisticService statisticService;

    @Autowired public StatisticResource( StatisticService statisticService) { this.statisticService = statisticService; } @GetMapping("/users") public List<UsersStatisticVM> getUsersStatistic() { return statisticService.getUsersStatistic(); } }
  118. @RestController @RequestMapping("/api/v1/statistics") public class StatisticResource { private final StatisticService statisticService;

    @Autowired public StatisticResource( StatisticService statisticService) { this.statisticService = statisticService; } @GetMapping("/users") public Flux<UsersStatisticVM> getUsersStatistic() { return Flux.fromIterable(statisticService .getUsersStatistic()); }
  119. @RestController @RequestMapping("/api/v1/statistics") public class StatisticResource { private final StatisticService statisticService;

    @Autowired public StatisticResource( StatisticService statisticService) { this.statisticService = statisticService; } @GetMapping("/users") public Flux<UsersStatisticVM> getUsersStatistic() { return Flux.fromIterable(statisticService .getUsersStatistic()); }
  120. Project Reactor + Netty Integration

  121. Why Netty?

  122. Non-Blocking,Reactive Server

  123. Reactive Architecture

  124. Data Base Connection Pipe Gitter Connection Pipe

  125. Refactoring Structure

  126. 1. Blocking I/O Refactoring 2. Service Refactoring 3. Endpoints Refactoring

    4. Pipeline Testing
  127. None
  128. Blocking I/O Refactoring

  129. None
  130. Reactive DB Access via ReactiveCrudRepository<T, ID>

  131. 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(); }
  132. 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(); }
  133. Code session

  134. .publishOn() public static <S, V> Mono<V> mono( Mono<S> input, Function<...>

    transformer ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); }
  135. .publishOn() public static <S, V> Mono<V> mono( Mono<S> input, Function<...>

    transformer ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); }
  136. .publishOn() public static <S, V> Mono<V> mono( Mono<S> input, Function<...>

    transformer ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); }
  137. .publishOn() public static <S, V> Mono<V> mono( Mono<S> input, Function<...>

    transformer ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); }
  138. .publishOn() public static <S, V> Mono<V> mono( Mono<S> input, Function<...>

    transformer ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); }
  139. .publishOn() public static <S, V> Mono<V> mono( Mono<S> input, Function<...>

    transformer ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); }
  140. public static <S, V> Mono<V> mono( Mono<S> input, Function<...> transformer

    ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); } .publishOn()
  141. public static <S, V> Mono<V> mono( Mono<S> input, Function<...> transformer

    ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); } .subscribeOn() MonoProcessor
  142. public static <S, V> Mono<V> mono( Mono<S> input, Function<...> transformer

    ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); } .subscribeOn() MonoProcessor
  143. public static <S, V> Mono<V> mono( Mono<S> input, Function<...> transformer

    ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); } MonoProcessor .subscribeOn() …subscribe();
  144. public static <S, V> Mono<V> mono( Mono<S> input, Function<...> transformer

    ) { MonoProcessor<V> monoProcessor = MonoProcessor.create(); input .publishOn(Schedulers.elastic()) .transform(transformer) .subscribe(monoProcessor); return monoProcessor .subscribeOn(Schedulers.elastic()); } …subscribe(); MonoProcessor .subscribeOn() …subscribe();
  145. Service Refactoring

  146. Gitter Service

  147. Reactive WebClient out of the box

  148. public interface WebClient { UriSpec<RequestHeadersSpec<?>> get(); //omitted... static WebClient create()

    { return new DefaultWebClientBuilder().build(); } interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> { //omitted... Mono<ClientResponse> exchange(); //omitted... } interface ResponseSpec { <T> Mono<T> bodyToMono(Class<T> bodyType); <T> Flux<T> bodyToFlux(Class<T> elementType); <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType); <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> element } }
  149. public interface WebClient { UriSpec<RequestHeadersSpec<?>> get(); //omitted... static WebClient create()

    { return new DefaultWebClientBuilder().build(); } interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> { //omitted... Mono<ClientResponse> exchange(); //omitted... } interface ResponseSpec { <T> Mono<T> bodyToMono(Class<T> bodyType); <T> Flux<T> bodyToFlux(Class<T> elementType); <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType); <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> element } }
  150. None
  151. Code Session

  152. @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource = Flux.from(

    gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  153. @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource = Flux.from(

    gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } .onBP()
  154. @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource = Flux.from(

    gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } .onBP()
  155. .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  156. .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  157. .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  158. .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } …subscribe();
  159. .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } …subscribe(); …subscribe();
  160. Message Service

  161. None
  162. Code Session

  163. @Override public Flux<MessageVM> latest() { return chatClient.stream() .transform(MessageMapper ::toViewModelUnits );

    } @Override public Flux<MessageVM> latest() { return chatClient.stream() .transform(MessageMapper ::toViewModelUnits ); } @Override public Flux<MessageVM> latest() { return chatClient.stream() .transform(MessageMapper ::toViewModelUnits ); } .transform()
  164. Statistics Service

  165. None
  166. Code Session

  167. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); }
  168. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform()
  169. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform()
  170. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } Channel .materialize()
  171. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform()
  172. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform()
  173. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform()
  174. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform()
  175. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .materialize() .transform()
  176. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .transform() .materialize()
  177. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } Channel .materialize()
  178. Channel @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker

    ) { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .materialize()
  179. @Autowired public DefaultMessageService( MessageRepository messageRepository, ChatService<MessageResponse> chatClient, MessageBroker messageBroker )

    { this.chatClient = chatClient; Flux<Message> saved = messageRepository .saveAll(chatClient.stream() .transform(MessageMapper::toDomainUnits) ); messageBroker.createChannel( "statisticChanged", saved.materialize() ); } .materialize() Channel
  180. Code Session

  181. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  182. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  183. .retry() Channel this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof

    USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  184. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  185. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  186. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  187. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  188. .retry() Channel this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof

    USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  189. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  190. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  191. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  192. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel .dematerialize()
  193. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); Channel .retry() .dematerialize()
  194. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) .mergeWith .defer(statistic)
  195. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) .mergeWith .defer(statistic) …subscribe();
  196. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) .mergeWith .defer(statistic) …subscribe();
  197. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) …subscribe(); this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .mergeWith .defer(statistic)
  198. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) …subscribe();
  199. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) …subscribe();
  200. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .flatMap() .cache(1) …subscribe();
  201. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); …subscribe(); .flatMap() .cache(1)
  202. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); Channel .retry() .dematerialize()
  203. this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() .dematerialize() .flatMap()
  204. .retry() .dematerialize() .flatMap() this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err

    instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  205. .dematerialize() .flatMap() .cache(1) this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err

    instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  206. .dematerialize() .flatMap() .cache(1) this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err

    instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  207. .flatMap() .cache(1) this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof

    USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  208. .flatMap() .cache(1) this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof

    USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  209. Code Session

  210. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  211. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  212. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  213. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  214. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  215. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  216. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .defaultIfEmpty(…) .zip(…) .fromDirect(…)
  217. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .defaultIfEmpty(…) .zip(…) .fromDirect(…)
  218. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null
  219. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null
  220. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null
  221. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null null
  222. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null
  223. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null
  224. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…) null
  225. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .map(…) .defaultIfEmpty(…) .zip(…)
  226. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .defaultIfEmpty(…) .zip(…) .fromDirect(…)
  227. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .defaultIfEmpty(…) .zip(…) .fromDirect(…)
  228. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM> topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .defaultIfEmpty(…) .zip(…) .fromDirect(…)
  229. private Mono<UsersStatisticVM> doGetUserStatistic Flux<UserVM> topActiveUser = userRepository .findAllOrderedByActivityDesc(PageRequest.o .map(UserMapper::toViewModelUnits); Flux<UserVM>

    topMentionedUser = userRepository .findAllOrderedByMentionDesc(PageRequest.of .map(UserMapper::toViewModelUnits); return Mono.fromDirect( Flux.zip( topActiveUser.defaultIfEmpty(EMPTY_USER), topMentionedUser.defaultIfEmpty(EMPTY_USER) UsersStatisticVM::new ) ); } .defaultIfEmpty(…) .zip(…) .fromDirect(…)
  230. Endpoints Refactoring

  231. None
  232. Reactive WS out of the box

  233. public interface WebSocketSession { String getId(); HandshakeInfo getHandshakeInfo(); DataBufferFactory bufferFactory();

    Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); default Mono<Void> close() { return close(CloseStatus.NORMAL); } Mono<Void> close(CloseStatus status); WebSocketMessage textMessage(String payload); WebSocketMessage binaryMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); WebSocketMessage pingMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); WebSocketMessage pongMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); }
  234. public interface WebSocketSession { String getId(); HandshakeInfo getHandshakeInfo(); DataBufferFactory bufferFactory();

    Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); default Mono<Void> close() { return close(CloseStatus.NORMAL); } Mono<Void> close(CloseStatus status); WebSocketMessage textMessage(String payload); WebSocketMessage binaryMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); WebSocketMessage pingMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); WebSocketMessage pongMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); }
  235. public interface WebSocketSession { String getId(); HandshakeInfo getHandshakeInfo(); DataBufferFactory bufferFactory();

    Flux<WebSocketMessage> receive(); Mono<Void> send(Publisher<WebSocketMessage> messages); default Mono<Void> close() { return close(CloseStatus.NORMAL); } Mono<Void> close(CloseStatus status); WebSocketMessage textMessage(String payload); WebSocketMessage binaryMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); WebSocketMessage pingMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); WebSocketMessage pongMessage( Function<DataBufferFactory, DataBuffer> payloadFactory); }
  236. Code session

  237. @Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  238. @Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  239. @Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  240. @Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  241. .merge() @Override public Publisher<?> handle( Publisher<Object> in ) { return

    Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); } network
  242. network .merge() @Override public Publisher<?> handle( Publisher<Object> in ) {

    return Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); }
  243. network .merge() @Override public Publisher<?> handle( Publisher<Object> in ) {

    return Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); }
  244. @Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  245. Pipeline Testing

  246. StepVerifier

  247. StepVerifier .create(myPublisher) .expectNext(“x”, “y”) .expectComplete() .verify();

  248. StepVerifier .create(myPublisher) .expectNext(“x”, “y”) .expectComplete() .verify();

  249. StepVerifier .create(myPublisher) .expectNext(“x”, “y”) .expectComplete() .verify();

  250. StepVerifier .create(myPublisher) .expectNext(“x”, “y”) .expectComplete() .verify();

  251. StepVerifier .create(myPublisher) .expectNext(“x”, “y”) .expectComplete() .verify();

  252. Code session

  253. Demo

  254. None
  255. Checkpoints • Message Driven • Resilient • Responsive • Elastic

  256. SUMMARY

  257. Q&A

  258. Resources 1. Why Reactive? [http://www.oreilly.com/programming/ free/why-reactive.csp] 2. Operator Fusion Part

    1 [http://akarnokd.blogspot.com/ 2016/03/operator-fusion-part-1.html] 3. Reactive Streams Specification [http://www.reactive- streams.org/] 4. Learn Project Reactor [http://projectreactor.io/learn]
  259. Thank you!