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

Going Reactive with Spring (JugSaxonyDay 2017)

Going Reactive with Spring (JugSaxonyDay 2017)

Christoph Strobl

September 29, 2017
Tweet

More Decks by Christoph Strobl

Other Decks in Programming

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

    View Slide

  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)

    View Slide

  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.

    View Slide

  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

    View Slide

  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/
    {…}

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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/

    View Slide

  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

    View Slide

  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/

    View Slide

  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
    Subscriber
    A processing stage - actually both a Subscriber and a Publisher.
    Processor
    One-to-one lifecycle of a Subscriber subscribing to a Publisher.
    Subscription

    View Slide

  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
    Subscriber
    Processor
    Subscription
    .subscribe(Subscriber)
    .onSubscribe(Subscription)
    .request(long)
    .onNext(T)
    Reactive Streams

    View Slide

  15. View Slide

  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 (0…n)
    Flux (0…n) Mono (0…1)
    Reactive Streams

    View Slide

  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 (0…n)
    Flux (0…n) Mono (0…1)
    Reactive Streams
    Mono findByEmail(String email);
    Flux findByName(String name);
    #jürgenized

    View Slide

  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);

    View Slide

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

    View Slide

  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

    View Slide

  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!

    View Slide

  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

    */

    Flux findAll(Class 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

    */

    Mono findOne(Query query, Class entityClass);

    View Slide

  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/
    Mono save(S entity);
    Mono save(Mono entity);
    Flux saveAll(Iterable entities);
    Flux saveAll(Publisher entities);
    Mono existsById(Mono id);
    Flux findAll();
    Mono count();
    Mono deleteById(ID id);

    View Slide

  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 {
    }
    Flux findAllByLastname(String lastname);
    Flux findAllByLastname(Mono lastname);
    Flux customQuery(String lastname);
    @Query("{lastname : ?0}");
    Flux findAllByLastname(String ln, Pageable page);

    View Slide

  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 fluxPersons(String name) {

    return repository.findAllByName(name);

    }


    View Slide

  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

    View Slide

  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 {
    }
    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 starks = Flux

    .fromStream(Stream.generate(() !-> 

    names[ramdom.nextInt(names.length)])

    .map(Person!::new));

    View Slide

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

    View Slide

  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 {
    Flux findAllByName(String name);
    }
    @RestController

    static class PersonController {


    PersonRepository repository;







    }
    @GetMapping("/")

    Flux fluxPersons(String name) {

    return repository.findAllByName(name);

    }


    View Slide

  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 {
    Flux findAllByName(String name);
    }
    @RestController

    static class PersonController {


    PersonRepository repository;







    }
    @GetMapping("/")

    Flux fluxPersons(String name) {

    return repository.findAllByName(name);

    }

    > cstrobl $ curl localhost:8080/?name=Eddard | jq
    [
    {
    "name": "Eddard",
    "id": "591014f2756bac231a23f3a0"
    },
    {
    "name": "Eddard",
    "id": "591014f7756bac231a23f3a5"
    }
    ]
    >

    View Slide

  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 {
    Observable findByName(String name);
    }
    @RestController

    static class PersonController {


    PersonRepository repository;


    @GetMapping("/rx")

    Observable rxPersons(String name) {

    return repository.findByName(name);

    }

    }

    View Slide

  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 {
    Observable findByName(String name);
    }
    @RestController

    static class PersonController {


    PersonRepository repository;


    @GetMapping("/rx")

    Observable 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"
    }
    ]
    >

    View Slide

  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 {
    @Tailable Flux findBy();
    }
    @RestController

    static class PersonController {


    PersonRepository repository;


    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux streamPersons() {

    return repository.findBy();

    }

    }

    View Slide

  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 {
    @Tailable Flux findBy();
    }
    @RestController

    static class PersonController {


    PersonRepository repository;


    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux 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"}

    View Slide

  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 streamPersons(ServerRequest request) {
    return ServerResponse.ok()

    .contentType(MediaType.TEXT_EVENT_STREAM)

    .body(personRepository.findBy(), Person.class);
    }
    @Bean

    RouterFunction routes(PersonHandler requestHandler) {

    return RouterFunctions

    .route(RequestPredicates.GET("/stream"), requestHandler!::streamPersons)
    }

    View Slide

  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);

    };

    }

    View Slide

  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.

    View Slide