Page 1
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Going with .
Christoph Strobl Pivotal Software Inc.
@stroblchristoph
Page 2
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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 threadswith back pressure as a key ingredient
that aims to ensure producers to not overwhelm consumers.(Rossen Stoyanchev)
Page 3
The next 60 Minutes
3 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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.
Page 4
A bit of recent history.
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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
Page 5
Imperative Applications
5 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
{…}
Page 6
Imperative Applications - Batching
6 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
{…}Batching
Page 7
Imperative Applications - Async
7 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
{…}
Dispatcher
Thread: Worker A
Thread: Worker B
Thread: Worker C
Page 8
Reactive Applications
8 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
{…}Subscriber PublisherStream
Page 9
Reactive Applications
9 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
{…}Subscriber PublisherStream
.block()
Page 10
NoSQL for the win!
10 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Page 11
11 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Iterator.next() Future.get()
Subscriber.onNext(t)
Remember
vs
Page 12
Reactor 3.1
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Page 13
Reactive Streams
13 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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
Page 14
14 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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
Page 16
Project Reactor
16 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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
Page 17
Project Reactor
17 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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
Page 18
Flux
18 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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);
Page 19
Mono
19 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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();
Page 20
Reactive Applications with Spring
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Spring Framework5
Project Reactor3.1
Spring DataKay
Page 21
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Apply familiar conceptsto an new way of expressing functionality.
With minimal fluff and surprise!
Page 22
Reactive Template (MongoDB)
22 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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);
Page 23
Reactive Repository
23 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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);
Page 24
Spring Data Reactive
24 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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);
Page 25
Spring WebFlux
25 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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);}
Page 26
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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
Page 27
Spring Data - Reactive Repository
27 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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));
Page 28
Spring Data - Reactive Repository
28 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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();
Page 29
Spring - WebFlux
29 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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);}
Page 30
Spring - WebFlux
30 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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" } ]
>
Page 31
RxJava types
31 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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); }}
Page 32
RxJava types
32 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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" } ]
>
Page 33
Tailable Cursors & Event Stream
33 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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(); }}
Page 34
Tailable Cursors & Event Stream
34 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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"}
Page 35
WebFlux - Router Functions
35 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
@Componentstatic class PersonHandler {
PersonRepository repository;
Mono<ServerResponse> streamPersons(ServerRequest request) { return ServerResponse.ok() .contentType(MediaType.TEXT_EVENT_STREAM) .body(personRepository.findBy(), Person.class); }
@BeanRouterFunction<ServerResponse> routes(PersonHandler requestHandler) { return RouterFunctions .route(RequestPredicates.GET("/stream"), requestHandler!::streamPersons) }
Page 36
Spring - WebClient
36 Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
@BeanWebClient client() { return WebClient.create("http:!//localhost:8080/");}@BeanCommandLineRunner run(WebClient client) { return (args) !-> { client.get() .uri("/stream") .retrieve() .bodyToFlux(Person.class) .subscribe(System.out!::println); };}
Page 37
Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software, Inc. and licensed under aCreative 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.