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. 2.

    • JSE at Levi9 • 6+ dev experience • Spring

    fan • Reactivity preacher /oleh.dokuka /OlegDokuka /OlehDokuka WHO AM I?
  2. 3.

    Agenda 1. Why Reactive? 2. Reactivity Generations 3. Reactive Streams

    4. Project Reactor 5. Reactivity in Action (Project Reactor + Spring 5)
  3. 18.
  4. 22.
  5. 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(); }
  6. 33.
  7. 34.
  8. 35.
  9. 36.
  10. 37.
  11. 38.
  12. 39.
  13. 40.
  14. 41.
  15. 42.
  16. 44.

    by

  17. 47.
  18. 49.
  19. 50.
  20. 52.
  21. 62.
  22. 64.
  23. 72.
  24. 73.
  25. 88.
  26. 89.
  27. 90.
  28. 91.
  29. 92.
  30. 94.
  31. 96.
  32. 98.
  33. 101.
  34. 102.
  35. 105.
  36. 106.
  37. 109.
  38. 111.
  39. 113.
  40. 114.
  41. 115.
  42. 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(); } }
  43. 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(); } }
  44. 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()); }
  45. 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()); }
  46. 121.
  47. 127.
  48. 129.
  49. 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(); }
  50. 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(); }
  51. 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()); }
  52. 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()); }
  53. 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()); }
  54. 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()); }
  55. 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()); }
  56. 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()); }
  57. 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()
  58. 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
  59. 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
  60. 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();
  61. 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();
  62. 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 } }
  63. 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 } }
  64. 150.
  65. 152.

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

    gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  66. 153.

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

    gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } .onBP()
  67. 154.

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

    gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } .onBP()
  68. 155.

    .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  69. 156.

    .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  70. 157.

    .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); }
  71. 158.

    .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } …subscribe();
  72. 159.

    .onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =

    Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } …subscribe(); …subscribe();
  73. 161.
  74. 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()
  75. 165.
  76. 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() ); }
  77. 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()
  78. 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()
  79. 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()
  80. 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()
  81. 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()
  82. 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()
  83. 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()
  84. 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()
  85. 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()
  86. 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()
  87. 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()
  88. 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
  89. 181.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  90. 182.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  91. 183.

    .retry() Channel this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof

    USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  92. 184.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  93. 185.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  94. 186.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  95. 187.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  96. 188.

    .retry() Channel this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof

    USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1);
  97. 189.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  98. 190.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  99. 191.

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

    .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel
  100. 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()
  101. 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()
  102. 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)
  103. 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();
  104. 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();
  105. 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)
  106. 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();
  107. 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();
  108. 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();
  109. 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)
  110. 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()
  111. 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()
  112. 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);
  113. 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);
  114. 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);
  115. 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);
  116. 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);
  117. 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(…)
  118. 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(…)
  119. 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(…)
  120. 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(…)
  121. 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(…)
  122. 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(…)
  123. 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(…)
  124. 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(…)
  125. 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
  126. 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
  127. 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
  128. 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
  129. 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
  130. 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
  131. 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
  132. 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(…)
  133. 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(…)
  134. 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(…)
  135. 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(…)
  136. 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(…)
  137. 231.
  138. 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); }
  139. 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); }
  140. 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); }
  141. 237.

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

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  142. 238.

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

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  143. 239.

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

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  144. 240.

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

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  145. 241.

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

    Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); } network
  146. 242.

    network .merge() @Override public Publisher<?> handle( Publisher<Object> in ) {

    return Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); }
  147. 243.

    network .merge() @Override public Publisher<?> handle( Publisher<Object> in ) {

    return Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); }
  148. 244.

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

    messageService.latest(), statisticService .usersStatisticStream() ); } network .merge()
  149. 253.
  150. 254.
  151. 256.
  152. 257.

    Q&A

  153. 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]
  154. 259.