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

Going Reactive with Spring (JugSaxonyDay 2017)

Going Reactive with Spring (JugSaxonyDay 2017)

B0e9230af37f3a883c88d3f11c00e174?s=128

Christoph Strobl

September 29, 2017
Tweet

Transcript

  1. Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Going with . Christoph Strobl
 Pivotal Software Inc.
 @stroblchristoph
  2. Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ In a nutshell, reactive programming is about
 non-blocking, event-driven applications
 that scale with a small number of threads
 with back pressure as a key ingredient
 that aims to ensure producers to not overwhelm consumers. (Rossen Stoyanchev)
  3. The next 60 Minutes 3 Unless otherwise indicated, these slides

    are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ - A bit of recent history. - Project Reactor / Spring Data Kay / Spring Framework 5. - Some Code.
  4. A bit of recent history. Unless otherwise indicated, these slides

    are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Sept 28 5.0 GA Dec 2017 Oct 2017 Kay GA 2.0 GA Sept 25 3.1 GA Sept 29 5.0 M1 Jul 2016 Kay M1 Sept 2016 Nov 2016 3.0 GA Sept 2015 Sept 21 9
  5. Imperative Applications 5 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ {…}
  6. Imperative Applications - Batching 6 Unless otherwise indicated, these slides

    are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ {…} Batching
  7. Imperative Applications - Async 7 Unless otherwise indicated, these slides

    are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ {…} Dispatcher Thread: Worker A Thread: Worker B Thread: Worker C
  8. Reactive Applications 8 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ {…} Subscriber Publisher Stream
  9. Reactive Applications 9 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ {…} Subscriber Publisher Stream .block()
  10. NoSQL for the win! 10 Unless otherwise indicated, these slides

    are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
  11. 11 Unless otherwise indicated, these slides are © 2013-2017 Pivotal

    Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Iterator.next() Future.get() Subscriber.onNext(t) Remember vs
  12. Reactor 3.1 Unless otherwise indicated, these slides are © 2013-2017

    Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
  13. Reactive Streams 13 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Provider of a potentially unbounded number of sequenced elements. Publisher<T> Subscriber<T> A processing stage - actually both a Subscriber and a Publisher. Processor<T,R> One-to-one lifecycle of a Subscriber subscribing to a Publisher. Subscription
  14. 14 Unless otherwise indicated, these slides are © 2013-2017 Pivotal

    Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Publisher<T> Subscriber<T> Processor<T,R> Subscription .subscribe(Subscriber) .onSubscribe(Subscription) .request(long) .onNext(T) Reactive Streams
  15. None
  16. Project Reactor 16 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Publisher<T> (0…n) Flux<T> (0…n) Mono<T> (0…1) Reactive Streams
  17. Project Reactor 17 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Publisher<T> (0…n) Flux<T> (0…n) Mono<T> (0…1) Reactive Streams Mono<User> findByEmail(String email); Flux<User> findByName(String name); #jürgenized
  18. Flux 18 Unless otherwise indicated, these slides are © 2013-2017

    Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ [fluhks] - a flowing or flow Flux.just("red", "white", "blue") .flatMap(v !-> Mono.fromCallable(blockingStuff(v)) .subscribeOn(Schedulers.elastic())) .collect(Result!::new, Result!::add) .doOnNext(Result!::stop) .subscribe(doWithResult);
  19. Mono 19 Unless otherwise indicated, these slides are © 2013-2017

    Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ [mon-oh] - alone, single, one Mono.fromCallable(blockingStuff()) .map(v !-> unwarp(v)) .subscribe();
  20. Reactive Applications with Spring Unless otherwise indicated, these slides are

    © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Framework
 5 Project Reactor
 3.1 Spring Data
 Kay
  21. Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Apply familiar concepts
 to an new way of expressing functionality.
 With minimal fluff and surprise!
  22. Reactive Template (MongoDB) 22 Unless otherwise indicated, these slides are

    © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ /**
 * Query for a {@link Flux} of objects of type T from the specified collection.
 *
 * @param entityClass the parametrized type of the returned {@link Flux}.
 * @param collectionName name of the collection to retrieve the objects from
 * @return the converted collection
 */
 <T> Flux<T> findAll(Class<T> entityClass, String collectionName); /**
 * Map the results of an ad-hoc query on the collection for the entity class to a 
 * single instance of an object of the
 * specified type. 
 * @param query the query class that specifies the criteria 
 * @param entityClass the parametrized type of the returned {@link Mono}.
 * @return the converted object
 */
 <T> Mono<T> findOne(Query query, Class<T> entityClass);
  23. Reactive Repository 23 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ <S extends T> Mono<S> save(S entity); <S extends T> Mono<S> save(Mono<S> entity); <S extends T> Flux<S> saveAll(Iterable<S> entities); <S extends T> Flux<S> saveAll(Publisher<S> entities); Mono<Boolean> existsById(Mono<ID> id); Flux<T> findAll(); Mono<Long> count(); Mono<Void> deleteById(ID id);
  24. Spring Data Reactive 24 Unless otherwise indicated, these slides are

    © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface Repo extends ReactiveCrudRepository<Person, String> { } Flux<Person> findAllByLastname(String lastname); Flux<Person> findAllByLastname(Mono<String> lastname); Flux<Person> customQuery(String lastname); @Query("{lastname : ?0}"); Flux<Person> findAllByLastname(String ln, Pageable page);
  25. Spring WebFlux 25 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 
 
 
 
 
 } @GetMapping("/")
 Flux<Person> fluxPersons(String name) {
 return repository.findAllByName(name);
 }

  26. Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ It’s Demo Time https://github.com/christophstrobl/going-reactive-with-spring-data
  27. Spring Data - Reactive Repository 27 Unless otherwise indicated, these

    slides are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { } Flux.interval(Duration.ofSeconds(1))
 .zipWith(starks)
 .map(Tuple2!::getT2)
 .flatMap(repository!::save)
 .subscribe(); String[] names = { "Eddard", "Catelyn", "Jon", "Rob", "Sansa", "Aria", "Bran", "Rickon" };
 Flux<Person> starks = Flux
 .fromStream(Stream.generate(() !-> 
 names[ramdom.nextInt(names.length)])
 .map(Person!::new));
  28. Spring Data - Reactive Repository 28 Unless otherwise indicated, these

    slides are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { } Flux.interval(Duration.ofSeconds(1))
 .zipWith(starks)
 .map(Tuple2!::getT2)
 .flatMap(repository!::save)
 .subscribe(); String[] names = { "Eddard", "Catelyn", "Jon", "Rob", "Sansa", "Aria", "Bran", "Rickon" };
 Flux<Person> starks = Flux
 .fromStream(Stream.generate(() !-> 
 names[ramdom.nextInt(names.length-1)])
 .map(Person!::new)); > cstrobl $ ./bin/mongo --port 52291 > use test switched to db test { "_id" : ObjectId("591009c5ed68a820fb9956a5"), "name" : "Sansa", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009c6ed68a820fb9956a6"), "name" : "Rob", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009c7ed68a820fb9956a7"), "name" : "Sansa", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009c8ed68a820fb9956a8"), "name" : "Rob", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009c9ed68a820fb9956a9"), "name" : "Jon", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009caed68a820fb9956aa"), "name" : "Aria", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009cbed68a820fb9956ab"), "name" : "Rob", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009cced68a820fb9956ac"), "name" : "Jon", "_class" : "com.example.DemoApplication$Person" } { "_id" : ObjectId("591009cded68a820fb9956ad"), "name" : "Sansa", "_class" : "com.example.DemoApplication$Person" } > db.person.find();
  29. Spring - WebFlux 29 Unless otherwise indicated, these slides are

    © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { Flux<Person> findAllByName(String name); } @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 
 
 
 
 
 } @GetMapping("/")
 Flux<Person> fluxPersons(String name) {
 return repository.findAllByName(name);
 }

  30. Spring - WebFlux 30 Unless otherwise indicated, these slides are

    © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { Flux<Person> findAllByName(String name); } @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 
 
 
 
 
 } @GetMapping("/")
 Flux<Person> fluxPersons(String name) {
 return repository.findAllByName(name);
 }
 > cstrobl $ curl localhost:8080/?name=Eddard | jq [ { "name": "Eddard", "id": "591014f2756bac231a23f3a0" }, { "name": "Eddard", "id": "591014f7756bac231a23f3a5" } ] >
  31. RxJava types 31 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { Observable<Person> findByName(String name); } @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 @GetMapping("/rx")
 Observable<Person> rxPersons(String name) {
 return repository.findByName(name);
 }
 }
  32. RxJava types 32 Unless otherwise indicated, these slides are ©

    2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { Observable<Person> findByName(String name); } @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 @GetMapping("/rx")
 Observable<Person> rxPersons(String name) {
 return repository.findByName(name);
 }
 } > cstrobl $ curl localhost:8080/rx?name=Rob | jq [ { "name": "Rob", "id": "590f6e99756bac18a8d62bfe" }, { "name": "Rob", "id": "590f6e9d756bac18a8d62c02" } { "name": "Rob", "id": "590f6e9e756bac18a8d62c03" } ] >
  33. Tailable Cursors & Event Stream 33 Unless otherwise indicated, these

    slides are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { @Tailable Flux<Person> findBy(); } @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) Flux<Person> streamPersons() {
 return repository.findBy();
 }
 }
  34. Tailable Cursors & Event Stream 34 Unless otherwise indicated, these

    slides are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ interface PersonRepository extends ReactiveCrudRepository<Person, String> { @Tailable Flux<Person> findBy(); } @RestController
 static class PersonController {
 
 PersonRepository repository;
 
 @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) Flux<Person> streamPersons() {
 return repository.findBy();
 }
 } > cstrobl $ curl localhost:8080/stream data:{"name":"Sansa","id":"5910192e756bac26079dd623"} data:{"name":"Jon","id":"5910192f756bac26079dd624"} data:{"name":"Catelyn","id":"59101930756bac26079dd625"} data:{"name":"Eddard","id":"59101931756bac26079dd626"} data:{"name":"Jon","id":"59101932756bac26079dd627"} data:{"name":"Eddard","id":"59101933756bac26079dd628"}
  35. WebFlux - Router Functions 35 Unless otherwise indicated, these slides

    are © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ @Component
 static class PersonHandler { PersonRepository repository; Mono<ServerResponse> streamPersons(ServerRequest request) { return ServerResponse.ok()
 .contentType(MediaType.TEXT_EVENT_STREAM)
 .body(personRepository.findBy(), Person.class); } @Bean
 RouterFunction<ServerResponse> routes(PersonHandler requestHandler) { 
 return RouterFunctions
 .route(RequestPredicates.GET("/stream"), requestHandler!::streamPersons) }
  36. Spring - WebClient 36 Unless otherwise indicated, these slides are

    © 2013-2017 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ @Bean
 WebClient client() {
 return WebClient.create("http:!//localhost:8080/");
 }
 
 @Bean
 CommandLineRunner run(WebClient client) {
 
 return (args) !-> {
 
 client.get()
 .uri("/stream")
 .retrieve()
 .bodyToFlux(Person.class)
 .subscribe(System.out!::println);
 };
 }
  37. Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive is about efficient resource usage. Even if backed with familiar concepts and framework support, 
 it is no free lunch. The price is complexity.