Slide 1

Slide 1 text

Reactive Spring Sébastien Deleuze - @sdeleuze Brian Clozel - @bclozel

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

2 ∞

Slide 4

Slide 4 text

2 ∞

Slide 5

Slide 5 text

2 ∞

Slide 6

Slide 6 text

2 ∞

Slide 7

Slide 7 text

Blocking + Thread pools 3 HTTP request HTTP response ⏳ ⚙ ⏳ ✍ ⏳ Thread

Slide 8

Slide 8 text

Non-blocking and event-loop 4 IO Selector Thread Worker Threads ⚙ ⚙ ✍ ✍ ⚙ ⚙ ✍ ✍ ⚙ ⚙ ✍

Slide 9

Slide 9 text

5 From blocking to event-based Neutral to latency

Slide 10

Slide 10 text

6 Reactive Foundations

Slide 11

Slide 11 text

7 Going Reactive More for scalability and stability than for speed

Slide 12

Slide 12 text

Reactive Streams 8 Publisher Subscriber

Slide 13

Slide 13 text

Reactive Streams 8 Publisher Subscriber subscribe

Slide 14

Slide 14 text

Reactive Streams 8 Publisher Subscriber request(n) Backpressure

Slide 15

Slide 15 text

Reactive Streams 8 Publisher Subscriber Backpressure onNext(data) onNext(data) onNext(data)

Slide 16

Slide 16 text

Reactive Streams 8 Publisher Subscriber Backpressure Error|Complete

Slide 17

Slide 17 text

9 public interface Publisher { void subscribe(Subscriber super T> s); } public interface Subscriber { void onSubscribe(Subscription s); void onNext(T t); void onError(Throwable t); void onComplete(); } public interface Subscription { void request(long n); void cancel(); } public interface Processor extends Subscriber, Publisher { } Reactive Streams API

Slide 18

Slide 18 text

10 Reactive Streams based libraries:

Slide 19

Slide 19 text

10 Reactive Streams based libraries: RxJava Reactor Akka Streams

Slide 20

Slide 20 text

11 Reactor is a reactive API based on reactive streams and Java 8

Slide 21

Slide 21 text

12 Flux Mono

Slide 22

Slide 22 text

13 Flux is a Publisher for 0..n elements

Slide 23

Slide 23 text

14 Flux Mono

Slide 24

Slide 24 text

15 Mono is a Publisher for 0..1 element

Slide 25

Slide 25 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message));

Slide 26

Slide 26 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message)); Mono fetchWeather(String city);

Slide 27

Slide 27 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message)); times out and emits an error after 2 sec

Slide 28

Slide 28 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message)); logs a message in case of errors

Slide 29

Slide 29 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message)); switches to a different service in case of error

Slide 30

Slide 30 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message)); transforms a weather instance into a String message

Slide 31

Slide 31 text

16 String location = "Lyon, France"; mainService.fetchWeather(location) .timeout(Duration.ofSeconds(2)) .doOnError(ex -> logger.error(ex.getMessage())) .onErrorResume(ex -> backupService.fetchWeather(location)) .map(w -> String.format("Weather in %s is %s", w.getLocation(), w.getDescription())) .subscribe(message -> logger.info(message)); triggers the processing of the chain

Slide 32

Slide 32 text

17 Building Reactive applications

Slide 33

Slide 33 text

18 interface ReactiveUserRepository { Mono findOne(String id); Flux findAll(); Mono save(Mono user); } interface UserRepository { User findOne(String id); List findAll(); void save(User user); } Blocking API Reactive API

Slide 34

Slide 34 text

19 interface UserRepository { User findOne(String id); List findAll(); void save(User user); } Blocking API

Slide 35

Slide 35 text

19 interface UserRepository { User findOne(String id); List findAll(); void save(User user); } Blocking API Method returns when whole list is received

Slide 36

Slide 36 text

19 interface UserRepository { User findOne(String id); List findAll(); void save(User user); } Blocking API Throws exception if an error happens

Slide 37

Slide 37 text

20 interface ReactiveUserRepository { Mono findOne(String id); Flux findAll(); Mono save(Mono user); } interface UserRepository { User findOne(String id); List findAll(); void save(User user); } Blocking API Reactive API

Slide 38

Slide 38 text

21 interface ReactiveUserRepository { Mono findOne(String id); Flux findAll(); Mono save(Mono user); } Reactive API

Slide 39

Slide 39 text

21 interface ReactiveUserRepository { Mono findOne(String id); Flux findAll(); Mono save(Mono user); } Reactive API Method returns immediately!

Slide 40

Slide 40 text

21 interface ReactiveUserRepository { Mono findOne(String id); Flux findAll(); Mono save(Mono user); } Reactive API Elements are received as soon as they are available, ending with a success "onComplete" event.

Slide 41

Slide 41 text

21 interface ReactiveUserRepository { Mono findOne(String id); Flux findAll(); Mono save(Mono user); } Reactive API Mono means error or success,
 no data will be received

Slide 42

Slide 42 text

22 Flux: Async collection vs. Stream of data

Slide 43

Slide 43 text

23 Flux

Slide 44

Slide 44 text

24 Flux as a JSON array

Slide 45

Slide 45 text

24 GET /users … 200 OK
 Content-Type: application/json [ {"a": "foo", "b": "bar"}, {"a": "baz", "b": "boo"} ] Flux as a JSON array

Slide 46

Slide 46 text

25 Flux as Server Sent Events

Slide 47

Slide 47 text

25 GET /users … 200 OK
 Content-Type: text/event-stream data: {"a": "foo", "b": "bar"} data: {"a": "baz", "b": "boo"} data: {"a": "dev", "b": "oxx"} data: {"a": "spr", "b": "ing"} data: {"a": "lov", "b": "es"} data: {"a": "and", "b": "more"} data: {"a": "data", "b": "coming"} Flux as Server Sent Events

Slide 48

Slide 48 text

26 Flux as a JSON stream

Slide 49

Slide 49 text

26 GET /users … 200 OK
 Content-Type: application/stream+json {"a": "foo", "b": "bar"} {"a": "baz", "b": "boo"} {"a": "dev", "b": "oxx"} {"a": "spr", "b": "ing"} {"a": "lov", "b": "es"} {"a": "and", "b": "more"} {"a": "data", "b": "coming"} Flux as a JSON stream

Slide 50

Slide 50 text

27 Reactive Spring

Slide 51

Slide 51 text

28 Spring Framework 5 uses Reactor for its reactive foundations

Slide 52

Slide 52 text

29

Slide 53

Slide 53 text

29

Slide 54

Slide 54 text

30 With Reactive Spring, you can use your reactive library of choice for your application:

Slide 55

Slide 55 text

30 With Reactive Spring, you can use your reactive library of choice for your application: RxJava Reactor Akka Streams

Slide 56

Slide 56 text

31 Reactor

Slide 57

Slide 57 text

31 Reactor

Slide 58

Slide 58 text

32

Slide 59

Slide 59 text

32 @Controller, @RequestMapping Spring MVC Servlet API Servlet Container

Slide 60

Slide 60 text

32 @Controller, @RequestMapping Spring MVC Servlet API Servlet Container Servlet 3.1, Netty, Undertow Spring WebFlux HTTP / Reactive Streams

Slide 61

Slide 61 text

32 @Controller, @RequestMapping Spring MVC Servlet API Servlet Container Router functions Servlet 3.1, Netty, Undertow Spring WebFlux HTTP / Reactive Streams

Slide 62

Slide 62 text

33 Flux WebClient Streaming API

Slide 63

Slide 63 text

33 Flux ✔ Reactive API ✔ Can consume (infinite) streams WebClient Streaming API

Slide 64

Slide 64 text

34 Flux.zip(tweets, issues) WebClient Streaming API REST API WebClient

Slide 65

Slide 65 text

34 Flux.zip(tweets, issues) ✔ Chain and compose WebClient Streaming API REST API WebClient

Slide 66

Slide 66 text

35 WebClient Web handler WebFlux

Slide 67

Slide 67 text

36 WebClient Web handler WebFlux

Slide 68

Slide 68 text

36 WebClient Web handler WebFlux ✔ Shared resources (event loop, buffers) ✔ Built-in mocking capabilities

Slide 69

Slide 69 text

37 WebClient, a nice RestTemplate alternative

Slide 70

Slide 70 text

37 WebClient, a nice RestTemplate alternative ✔ Fluent and flexible API ✔ Easy configuration

Slide 71

Slide 71 text

38 WebFlux demo!

Slide 72

Slide 72 text

39 https://github.com/bclozel/webflux-streaming-showcase quote-stream streaming-service GET /quotes
 "application/stream+json" GET /quotes/feed
 "text/event-stream" MongoDB

Slide 73

Slide 73 text

40 WebFlux, the functional way

Slide 74

Slide 74 text

41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction


Slide 75

Slide 75 text

41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 Mono> route(ServerRequest req)

Slide 76

Slide 76 text

41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 boolean test(ServerRequest req)

Slide 77

Slide 77 text

41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 Mono handle(ServerRequest req)

Slide 78

Slide 78 text

41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 Mono filter(ServerRequest req,
 HandlerFunction next)

Slide 79

Slide 79 text

41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction


Slide 80

Slide 80 text

42 // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... }

Slide 81

Slide 81 text

43 // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... } route( // Functional Java without static imports RequestPredicates. RequestPredicates.accept(TEXT_EVENT_STREAM)), path( ) { ... } ) .and( "/quotes/feed" RouterFunctions.

Slide 82

Slide 82 text

44 // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... } accept(TEXT_EVENT_STREAM)), path( ) { ... } ) .and( "/quotes/feed" // Functional Java with static imports route(

Slide 83

Slide 83 text

45 // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux fetchQuotesStream() { ... } accept(TEXT_EVENT_STREAM) { ... } and "/quotes/feed" // Functional Kotlin router { }

Slide 84

Slide 84 text

46 @RestController public class UserController { @GetMapping( ) public Flux () { // ... } @GetMapping( ) public Mono (@PathVariable String userId) { // ... } } fetchAll "/users" "/users/{id}" fetch

Slide 85

Slide 85 text

47 @Configuration class ApplicationRoutes(val userHandler: UserHandler, val blogHandler: BlogHandler, val shopRepository: ShopRepository) { @Bean fun appRouter() = router { GET( , userHandler:: ) GET( , userHandler:: ) } } fetchAll "/users" "/users/{id}" fetch @Bean fun nestedRouter() = router { @Bean fun dynamicRouter() = router { ... ... } }

Slide 86

Slide 86 text

48 @Bean fun nestedRouter() = router { @Bean fun dynamicRouter() = router {... } } ("/blog" and accept(TEXT_HTML)).nest { GET("/", blogHandler::findAllView) GET("/{slug}", blogHandler::findOneView) } ("/api/blog" and accept(APPLICATION_JSON)).nest { GET("/", blogHandler::findAll) GET("/{id}", blogHandler::findOne) POST("/", blogHandler::create) }

Slide 87

Slide 87 text

49 @Bean fun dynamicRouter() = router { } shopRepository.findAll() .toIterable() .forEach { shop -> GET("/${shop.id}") { req -> shopHandler.homepage(shop, req) } }

Slide 88

Slide 88 text

50 Spring ❤ Kotlin

Slide 89

Slide 89 text

51

Slide 90

Slide 90 text

52 inline fun ServerResponse.BodyBuilder.body(publisher: Publisher) = body(publisher, T::class.java) ServerResponse extensions provided in spring-webflux.jar

Slide 91

Slide 91 text

52 inline fun ServerResponse.BodyBuilder.body(publisher: Publisher) = body(publisher, T::class.java) ServerResponse extensions provided in spring-webflux.jar Extend existing Spring Java type

Slide 92

Slide 92 text

52 inline fun ServerResponse.BodyBuilder.body(publisher: Publisher) = body(publisher, T::class.java) ServerResponse extensions provided in spring-webflux.jar Extend existing Spring Java type Mono findAll(ServerRequest request) { return ServerResponse.ok().body(repository.findAll(), User.class); } Need to specify the type because of type erasure

Slide 93

Slide 93 text

52 fun findAll(req: ServerRequest) = ok().body(repository.findAll()) No need to specify explicitly types thanks
 to Kotlin reified type parameters inline fun ServerResponse.BodyBuilder.body(publisher: Publisher) = body(publisher, T::class.java) ServerResponse extensions provided in spring-webflux.jar Extend existing Spring Java type

Slide 94

Slide 94 text

53 Kotlin extensions bundled with Spring 5

Slide 95

Slide 95 text

53 Kotlin extensions bundled with Spring 5 Reactor (via reactor-kotlin-extensions) ApplicationContext WebFlux client WebFlux server Spring MVC RestTemplate JDBC Spring Data extensions are coming

Slide 96

Slide 96 text

54 start.spring.io

Slide 97

Slide 97 text

55 start.spring.io

Slide 98

Slide 98 text

56 Thank You!