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.

Sébastien Deleuze

April 24, 2017
Tweet

More Decks by Sébastien Deleuze

Other Decks in Programming

Transcript

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

    View Slide

  2. 2

    View Slide

  3. 2

    View Slide

  4. 2


    View Slide

  5. 2



    View Slide

  6. 2



    View Slide

  7. Blocking + Thread pools
    3
    HTTP request
    HTTP response






    Thread

    View Slide

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


    ⚙ ⚙ ✍
    ✍ ⚙

    ✍ ✍ ⚙
    ⚙ ✍

    View Slide

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

    View Slide

  10. 6
    Reactive Foundations

    View Slide

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

    View Slide

  12. Reactive Streams
    8
    Publisher Subscriber

    View Slide

  13. Reactive Streams
    8
    Publisher Subscriber
    subscribe

    View Slide

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

    View Slide

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

    View Slide

  16. Reactive Streams
    8
    Publisher Subscriber
    Backpressure
    Error|Complete

    View Slide

  17. 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

    View Slide

  18. 10
    Reactive Streams based libraries:

    View Slide

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

    View Slide

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

    View Slide

  21. 12
    Flux
    Mono

    View Slide

  22. 13
    Flux is a Publisher for 0..n elements

    View Slide

  23. 14
    Flux
    Mono

    View Slide

  24. 15
    Mono is a Publisher for 0..1 element

    View Slide

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

    View Slide

  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 fetchWeather(String city);

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  32. 17
    Building Reactive applications

    View Slide

  33. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. 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

    View Slide

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

    View Slide

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

    View Slide

  40. 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.

    View Slide

  41. 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

    View Slide

  42. 22
    Flux:
    Async collection vs. Stream of data

    View Slide

  43. 23
    Flux

    View Slide

  44. 24
    Flux as a JSON array

    View Slide

  45. 24
    GET /users

    200 OK

    Content-Type: application/json
    [
    {"a": "foo", "b": "bar"},
    {"a": "baz", "b": "boo"}
    ]
    Flux as a JSON array

    View Slide

  46. 25
    Flux as Server Sent Events

    View Slide

  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 as Server Sent Events

    View Slide

  48. 26
    Flux as a JSON stream

    View Slide

  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 as a JSON stream

    View Slide

  50. 27
    Reactive Spring

    View Slide

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

    View Slide

  52. 29

    View Slide

  53. 29

    View Slide

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

    View Slide

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

    View Slide

  56. 31
    Reactor

    View Slide

  57. 31
    Reactor




    View Slide

  58. 32

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  62. 33
    Flux WebClient
    Streaming API

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  66. 35
    WebClient
    Web handler
    WebFlux

    View Slide

  67. 36
    WebClient
    Web handler
    WebFlux

    View Slide

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

    View Slide

  69. 37
    WebClient,
    a nice RestTemplate alternative

    View Slide

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

    View Slide

  71. 38
    WebFlux demo!

    View Slide

  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

    View Slide

  73. 40
    WebFlux,
    the functional way

    View Slide

  74. 41
    ‣ RouterFunction

    ‣ RequestPredicate

    ‣ HandlerFunction

    ‣ HandlerFilterFunction


    View Slide

  75. 41
    ‣ RouterFunction

    ‣ RequestPredicate

    ‣ HandlerFunction

    ‣ HandlerFilterFunction

    Mono> route(ServerRequest req)

    View Slide

  76. 41
    ‣ RouterFunction

    ‣ RequestPredicate

    ‣ HandlerFunction

    ‣ HandlerFilterFunction

    boolean test(ServerRequest req)

    View Slide

  77. 41
    ‣ RouterFunction

    ‣ RequestPredicate

    ‣ HandlerFunction

    ‣ HandlerFilterFunction

    Mono handle(ServerRequest req)

    View Slide

  78. 41
    ‣ RouterFunction

    ‣ RequestPredicate

    ‣ HandlerFunction

    ‣ HandlerFilterFunction

    Mono filter(ServerRequest req,

    HandlerFunction next)

    View Slide

  79. 41
    ‣ RouterFunction

    ‣ RequestPredicate

    ‣ HandlerFunction

    ‣ HandlerFilterFunction


    View Slide

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

    View Slide

  81. 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.

    View Slide

  82. 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(

    View Slide

  83. 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 {
    }

    View Slide

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

    View Slide

  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 {
    ...
    ...
    }
    }

    View Slide

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

    View Slide

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

    View Slide

  88. 50
    Spring ❤ Kotlin

    View Slide

  89. 51

    View Slide

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

    View Slide

  91. 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

    View Slide

  92. 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

    View Slide

  93. 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

    View Slide

  94. 53
    Kotlin extensions bundled with Spring 5

    View Slide

  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

    View Slide

  96. 54
    start.spring.io

    View Slide

  97. 55
    start.spring.io

    View Slide

  98. 56
    Thank You!

    View Slide