Share
Embed
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.
Oleh Dokuka
May 26, 2017
Transcript
259 slides
-
• JSE at Levi9 • 6+ dev experience • Spring
fan • Reactivity preacher /oleh.dokuka /OlegDokuka /OlehDokuka WHO AM I? -
Agenda 1. Why Reactive? 2. Reactivity Generations 3. Reactive Streams
4. Project Reactor 5. Reactivity in Action (Project Reactor + Spring 5) -
Publisher Subscriber public interface Subscriber<T> { public void onSubscribe(Subscription s);
public void onNext(T t); public void onError(Throwable t); public void onComplete(); } -
Publisher Subscriber Subscription public interface Subscription { public void request(long
n); public void cancel(); } -
Publisher Subscriber Subscription public interface Subscription { public void request(long
n); public void cancel(); } -
Toolkit • Spring Framework 4 • Spring Boot • Spring
MVC • Spring Data JPA -
User Client Gitter Service Whole Application DB Layer Service Layer
Controllers Layer -
WebFlux @Controller, @RequestMapping,… Spring MVC Servlet API Servlet Container Spring
Web Reactive Reactive HTTP Servlet, Netty, Undertow -
@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(); } } -
@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(); } } -
@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()); } -
@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()); } -
1. Blocking I/O Refactoring 2. Service Refactoring 3. Endpoints Refactoring
4. Pipeline Testing -
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(); } -
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(); } -
.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()); } -
.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()); } -
.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()); } -
.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()); } -
.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()); } -
.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()); } -
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() -
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 -
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 -
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(); -
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(); -
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 } } -
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 } } -
@Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource = Flux.from(
gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } -
@Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource = Flux.from(
gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } .onBP() -
@Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource = Flux.from(
gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } .onBP() -
.onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =
Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } -
.onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =
Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } -
.onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =
Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } -
.onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =
Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } …subscribe(); -
.onBP() @Autowired public GitterService( GitterClient gitterClient ) { gitterMessageSource =
Flux.from( gitterClient .getMessages(null) ) .onBackpressureBuffer() .publish(1) .autoConnect(0); } …subscribe(); …subscribe(); -
@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() -
@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() ); } -
@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() -
@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() -
@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() -
@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() -
@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() -
@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() -
@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() -
@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() -
@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() -
@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() -
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() -
@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 -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
.retry() Channel 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); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
.retry() Channel 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); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() Channel .dematerialize() -
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() -
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) -
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(); -
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(); -
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) -
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); .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); .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); …subscribe(); .flatMap() .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() -
this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof USE) .dematerialize()
.flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); .retry() .dematerialize() .flatMap() -
.retry() .dematerialize() .flatMap() this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err
instanceof USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); -
.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); -
.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); -
.flatMap() .cache(1) this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof
USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); -
.flatMap() .cache(1) this.statisticPublisher = messageBroker .channel("statisticChanged") .retry(err -> err instanceof
USE) .dematerialize() .flatMap(s -> doGetUserStatistic()) .mergeWith(Mono .defer(this::doGetUserStatistic) ) .cache(1); -
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(…) -
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(…) -
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(…) -
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(…) -
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(…) -
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(…) -
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(…) -
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(…) -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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(…) -
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(…) -
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(…) -
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(…) -
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(…) -
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); } -
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); } -
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); } -
@Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(
messageService.latest(), statisticService .usersStatisticStream() ); } network .merge() -
@Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(
messageService.latest(), statisticService .usersStatisticStream() ); } network .merge() -
@Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(
messageService.latest(), statisticService .usersStatisticStream() ); } network .merge() -
@Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(
messageService.latest(), statisticService .usersStatisticStream() ); } network .merge() -
.merge() @Override public Publisher<?> handle( Publisher<Object> in ) { return
Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); } network -
network .merge() @Override public Publisher<?> handle( Publisher<Object> in ) {
return Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); } -
network .merge() @Override public Publisher<?> handle( Publisher<Object> in ) {
return Flux.merge( messageService.latest(), statisticService .usersStatisticStream() ); } -
@Override public Publisher<?> handle( Publisher<Object> in ) { return Flux.merge(
messageService.latest(), statisticService .usersStatisticStream() ); } network .merge() -
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]