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

Reactive Spring

Reactive Spring

This session will start with a Reactive Streams and Reactive APIs introduction, then will show concretely how you can build a reactive application using Spring 5.0, Spring Boot 2.0 and Java 8 with code and demos including a JSON REST API, Mustache templates based HTML rendering with Server-Sent Events push for live updates. We will also cover how to use Spring Data Reactive and the new ‘WebClient’ for processing data coming from remote services (REST or streaming APIs). Netty will be used as the underlying non-blocking HTTP engine for both client and server. I will also give you an overview of the Spring WebFlux functional Web API based on lambdas instead of annotation and of Spring 5.0 Kotlin native support.

5778521f67d80de0ee3b213e4f159a59?s=128

Sébastien Deleuze

April 24, 2017
Tweet

Transcript

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

  2. 2

  3. 2 ∞

  4. 2 ∞

  5. 2 ∞

  6. 2 ∞

  7. Blocking + Thread pools 3 HTTP request HTTP response ⏳

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

    ⚙ ✍ ✍ ⚙ ⚙ ✍ ✍ ⚙ ⚙ ✍
  9. 5 From blocking to event-based Neutral to latency

  10. 6 Reactive Foundations

  11. 7 Going Reactive More for scalability and stability than for

    speed
  12. Reactive Streams 8 Publisher Subscriber

  13. Reactive Streams 8 Publisher Subscriber subscribe

  14. Reactive Streams 8 Publisher Subscriber request(n) Backpressure

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

  16. Reactive Streams 8 Publisher Subscriber Backpressure Error|Complete

  17. 9 public interface Publisher<T> { void subscribe(Subscriber<? super T> s);

    } public interface Subscriber<T> { 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<T, R> extends Subscriber<T>, Publisher<R> { } Reactive Streams API
  18. 10 Reactive Streams based libraries:

  19. 10 Reactive Streams based libraries: RxJava Reactor Akka Streams

  20. 11 Reactor is a reactive API based on reactive streams

    and Java 8
  21. 12 Flux<T> Mono<T>

  22. 13 Flux<T> is a Publisher<T> for 0..n elements

  23. 14 Flux<T> Mono<T>

  24. 15 Mono<T> is a Publisher<T> for 0..1 element

  25. 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));
  26. 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<Weather> fetchWeather(String city);
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 17 Building Reactive applications

  33. 18 interface ReactiveUserRepository { Mono<User> findOne(String id); Flux<User> findAll(); Mono<Void>

    save(Mono<User> user); } interface UserRepository { User findOne(String id); List<User> findAll(); void save(User user); } Blocking API Reactive API
  34. 19 interface UserRepository { User findOne(String id); List<User> findAll(); void

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

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

    save(User user); } Blocking API Throws exception if an error happens
  37. 20 interface ReactiveUserRepository { Mono<User> findOne(String id); Flux<User> findAll(); Mono<Void>

    save(Mono<User> user); } interface UserRepository { User findOne(String id); List<User> findAll(); void save(User user); } Blocking API Reactive API
  38. 21 interface ReactiveUserRepository { Mono<User> findOne(String id); Flux<User> findAll(); Mono<Void>

    save(Mono<User> user); } Reactive API
  39. 21 interface ReactiveUserRepository { Mono<User> findOne(String id); Flux<User> findAll(); Mono<Void>

    save(Mono<User> user); } Reactive API Method returns immediately!
  40. 21 interface ReactiveUserRepository { Mono<User> findOne(String id); Flux<User> findAll(); Mono<Void>

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

    save(Mono<User> user); } Reactive API Mono<Void> means error or success,
 no data will be received
  42. 22 Flux<T>: Async collection vs. Stream of data

  43. 23 Flux<User>

  44. 24 Flux<User> as a JSON array

  45. 24 GET /users … 200 OK
 Content-Type: application/json [ {"a":

    "foo", "b": "bar"}, {"a": "baz", "b": "boo"} ] Flux<User> as a JSON array
  46. 25 Flux<User> as Server Sent Events

  47. 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<User> as Server Sent Events
  48. 26 Flux<User> as a JSON stream

  49. 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<User> as a JSON stream
  50. 27 Reactive Spring

  51. 28 Spring Framework 5 uses Reactor for its reactive foundations

  52. 29

  53. 29

  54. 30 With Reactive Spring, you can use your reactive library

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

    of choice for your application: RxJava Reactor Akka Streams
  56. 31 Reactor

  57. 31 Reactor

  58. 32

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

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

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

    functions Servlet 3.1, Netty, Undertow Spring WebFlux HTTP / Reactive Streams
  62. 33 Flux<Tweet> WebClient Streaming API

  63. 33 Flux<Tweet> ✔ Reactive API ✔ Can consume (infinite) streams

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

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

    REST API WebClient
  66. 35 WebClient Web handler WebFlux

  67. 36 WebClient Web handler WebFlux

  68. 36 WebClient Web handler WebFlux ✔ Shared resources (event loop,

    buffers) ✔ Built-in mocking capabilities
  69. 37 WebClient, a nice RestTemplate alternative

  70. 37 WebClient, a nice RestTemplate alternative ✔ Fluent and flexible

    API ✔ Easy configuration
  71. 38 WebFlux demo!

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

    MongoDB
  73. 40 WebFlux, the functional way

  74. 41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction


  75. 41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 Mono<HandlerFunction<ServerResponse>>

    route(ServerRequest req)
  76. 41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 boolean

    test(ServerRequest req)
  77. 41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 Mono<ServerResponse>

    handle(ServerRequest req)
  78. 41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction
 Mono<ServerResponse>

    filter(ServerRequest req,
 HandlerFunction<ServerResponse> next)
  79. 41 ‣ RouterFunction
 ‣ RequestPredicate
 ‣ HandlerFunction
 ‣ HandlerFilterFunction


  80. 42 // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux<Quote>

    fetchQuotesStream() { ... }
  81. 43 // Annotation-based Java @RequestMapping("/quotes/feed", produces = TEXT_EVENT_STREAM_VALUE) public Flux<Quote>

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

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

    fetchQuotesStream() { ... } accept(TEXT_EVENT_STREAM) { ... } and "/quotes/feed" // Functional Kotlin router { }
  84. 46 @RestController public class UserController { @GetMapping( ) public Flux<User>

    () { // ... } @GetMapping( ) public Mono<User> (@PathVariable String userId) { // ... } } fetchAll "/users" "/users/{id}" fetch
  85. 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 { ... ... } }
  86. 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) }
  87. 49 @Bean fun dynamicRouter() = router { } shopRepository.findAll() .toIterable()

    .forEach { shop -> GET("/${shop.id}") { req -> shopHandler.homepage(shop, req) } }
  88. 50 Spring ❤ Kotlin

  89. 51

  90. 52 inline fun <reified T : Any> ServerResponse.BodyBuilder.body(publisher: Publisher<T>) =

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

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

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

    explicitly types thanks
 to Kotlin reified type parameters inline fun <reified T : Any> ServerResponse.BodyBuilder.body(publisher: Publisher<T>) = body(publisher, T::class.java) ServerResponse extensions provided in spring-webflux.jar Extend existing Spring Java type
  94. 53 Kotlin extensions bundled with Spring 5

  95. 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
  96. 54 start.spring.io

  97. 55 start.spring.io

  98. 56 Thank You!