diff --git a/src/asciidoc/web-reactive.adoc b/src/asciidoc/web-reactive.adoc index 29ef1677eac..513c56374bb 100644 --- a/src/asciidoc/web-reactive.adoc +++ b/src/asciidoc/web-reactive.adoc @@ -1,6 +1,7 @@ [[web-reactive]] = Web Reactive Framework -This section provides basic information on the Spring Web Reactive support in Spring Framework 5. +This section provides basic information on the reactive programming +support for Web applications in Spring Framework 5. [[web-reactive-intro]] @@ -8,18 +9,20 @@ This section provides basic information on the Spring Web Reactive support in Sp [[web-reactive-programming]] -=== Reactive Programming +=== What is Reactive Programming? In plain terms reactive programming is about non-blocking applications that are asynchronous -and event-driven and require a small number of threads to scale. A key aspect of that -definition is the concept of backpressure which is a mechanism to ensure producers -don't overwhelm consumers. For example in a pipeline of reactive components that extends -from the database to the HTTP socket when the HTTP client is slow the data -repository slows down or stops until capacity frees up. +and event-driven and require a small number of threads to scale vertically (i.e. within the +JVM) rather than horizontally (i.e. through clustering). -From a programming model perspective reactive programming involves a major shift from imperative style logic -to a declarative composition of async logic. It is comparable to using `CompletableFuture` in Java 8 -and composing follow-up actions via lambda expressions. +A key aspect of the reactive applications is the concept of backpressure which is +a mechanism to ensure producers don't overwhelm consumers. For example in a pipeline +of reactive components extending from the database to the HTTP connection when the +HTTP client is slow the data repository also slows down or stops until capacity frees up. + +Reactive programming also involves a major shift from imperative style logic +to declarative async composition of logic. It is comparable to using `CompletableFuture` +in Java 8 and composing follow-up actions via lambda expressions. For a more extended introduction to reactive programming check the excellent multi-part series https://spring.io/blog/2016/06/07/notes-on-reactive-programming-part-i-the-reactive-landscape["Notes on Reactive Programming"] @@ -27,7 +30,7 @@ by Dave Syer. [[web-reactive-api]] -=== Spring Web Reactive Types +=== Reactive API and Building Blocks Spring Framework 5 embraces https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams[Reactive Streams] @@ -50,46 +53,44 @@ by Sebastien Deleuze. [[web-reactive-feature-overview]] == Spring Web Reactive Overview -Spring Framework 5 adds a new `spring-web-reactive` module that provides both reactive -client and server. +Spring Framework 5 includes a new `spring-web-reactive` module. The module contains support +for reactive HTTP and WebSocket clients as well as for reactive server web applications +including REST, HTML browser, and WebSocket style interactions. [[web-reactive-server]] -=== Reactive Web Server +=== Server-Side Reactive Web Support -The reactive web server is available in 2 flavors: +On the server-side the new reactive module supports 2 distinct programming models: -* With the same `@Controller` annotation-based programming model than Spring MVC -* With a new functional programming model using Java 8 lambdas +* Annotation-based with `@Controller` and the other annotations supported also with Spring MVC +* Functional, Java 8 lambda style routing and handling -When using Spring Web Reactive, regardless of the programming model you choose, your -application is executed on a reactive non-blocking engine. The diagram below shows how -Spring MVC and Spring Web Reactive compare side by side: +Both programming models are executed on the same reactive foundation that adapts +non-blocking HTTP runtimes to the Reactive Streams API. The diagram +below shows the server-side stack including traditional, Servlet-based +Spring MVC on the left from the `spring-web-mvc` module and also the +reactive stack on the right from the `spring-web-reactive` module. image::images/web-reactive-overview.png[width=720] -Spring Web Reactive makes use of Servlet 3.1 non-blocking I/O and runs on -Servlet 3.1 containers. It also runs on non-Servlet runtimes such as Netty and Undertow. -Each runtime is adapted to a set of shared, reactive `ServerHttpRequest` and -`ServerHttpResponse` abstractions that expose the request and response body -as `Flux` with full backpressure support on the read and the -write side. - -JSON or XML REST webservices are supported, as well as view rendering, server-sent events -and Websocket. +The new reactive stack can run on Servlet containers with support for the +Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as +Netty and Undertow. Each runtime is adapted to a reactive +`ServerHttpRequest` and `ServerHttpResponse` exposing the body of the +request and response as `Flux`, rather than +`InputStream` and `OutputStream`, with reactive backpressure. +REST-style JSON and XML serialization and deserialization is supported on top +as a `Flux`, and so is HTML view rendering and Server-Sent Events. [[web-reactive-server-annotation]] -==== Annotation-based programming model - -The `@Controller` programming model supported by Spring Web Reactive re-defines many of -the Spring MVC contracts such as `HandlerMapping` and `HandlerAdapter` to be asynchronous -and non-blocking and to operate on the reactive HTTP request and response. For this reason -Spring MVC and Spring Web Reactive cannot share any code. However they do share -many of the same algorithms. +==== Annotation-based Programming Model -The end result is a programming model identical to today's Spring MVC but -with support for reactive types and executed in a reactive manner. - -Here is an example of a reactive controller declared with annotations: +The same `@Controller` programming model and the same annotations used in Spring MVC +are also supported on the reactive side. The main difference is that the framework +contracts underneath -- i.e. `HandlerMapping`, `HandlerAdapter`, are +non-blocking and operate on the reactive `ServerHttpRequest` and `ServerHttpResponse` +rather than on the `HttpServletRequest` and `HttpServletResponse`. +Below is an example with a reactive controller: [source,java,indent=0] [subs="verbatim,quotes"] @@ -121,14 +122,12 @@ public class PersonController { ---- [[web-reactive-server-functional]] -==== Functional programming model - -The functional programming model uses Java 8 lambdas instead of annotations to allow you -to create a web application. It is built on top of simple but powerful building blocks like -`RouterFunction` and `HandlerFunction`. For more details, see this -https://spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework[blog post]. +==== Functional Programming Model -Here is an example of a Spring web functional controller: +The functional programming model uses Java 8 lambda style routing and request +handling instead of annotations. The main API contracts are functional interfaces named +`RouterFunction` and `HandlerFunction`. They are simple but powerful building blocks +for creating web applications. Below is an example of functional request handling: [source,java,indent=0] [subs="verbatim,quotes"] @@ -143,67 +142,71 @@ RouterFunctions .then(person -> ServerResponse.ok().body(Mono.just(person), Person.class)) .otherwiseIfEmpty(notFound); }) - .andRoute(GET("/person").and(accept(APPLICATION_JSON)), request -> ServerResponse.ok().body(repository.findAll(), Person.class)) - .andRoute(POST("/person").and(contentType(APPLICATION_JSON)), request -> ServerResponse.ok().build(repository.save(request.bodyToMono(Person.class)))); ---- -[[web-reactive-client]] -=== Reactive Web Client +For more on the functional programming model see the +https://spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework[M3 release blog post]. -Spring Framework 5 adds a new reactive `WebClient` in addition to the existing `RestTemplate` -and `AsyncRestTemplate`. In addition to a revised API, a big difference between -`AsyncRestTemplate` and the reactive `WebClient` is that the later allows to consume -streaming APIs like https://dev.twitter.com/streaming/overview[Twitter one] for example. -A `WebClient` instance use a `ClientHttpConnector` implementation to drive the underlying -supported HTTP client (e.g. Reactor Netty). This client is adapted to a set of shared, -reactive `ClientHttpRequest` and `ClientHttpResponse` abstractions that expose the request -and response body as `Flux` with full backpressure support on the read and -the write side. The `HttpMessageReader` and `HttpMessageWriter` abstractions are also used on -the client side for the serialization of a `Flux` of bytes to and from typed objects. +[[web-reactive-client]] +=== Client-Side Reactive Web Support -An example of using the `WebClient`: +Spring Framework 5 includes a functional, reactive `WebClient` that offers a fully +non-blocking and reactive alternative to the `RestTemplate`. It exposes network +input and output as a reactive `ClientHttpRequest` and `ClientHttpRespones` where +the body of the request and response is a `Flux` rather than an +`InputStream` and `OutputStream`. In addition it supports the same reactive JSON, XML, +and SSE serialization mechanism as on the server side so you can work with typed objects. +Below is an example of using the `WebClient` which requires a `ClientHttpConnector` +implementation to plug in a specific HTTP client such as Reactor Netty: [source,java,indent=0] [subs="verbatim,quotes"] ---- -// create an immutable instance of WebClient -WebClient webClient = WebClient.create(new ReactorClientHttpConnector()); +WebClient client = WebClient.create(new ReactorClientHttpConnector()); -ClientRequest request = ClientRequest.GET("http://example.com/accounts/{id}", 1L) - .accept(MediaType.APPLICATION_JSON).build(); +ClientRequest request = ClientRequest + .GET("http://example.com/accounts/{id}", 1L) + .accept(APPLICATION_JSON) + .build(); -Mono account = this.webClient - .exchange(request) - .then(response -> response.bodyToMono(Account.class)); +Mono account = client + .exchange(request) + .then(response -> response.bodyToMono(Account.class)); ---- -A `WebSocketClient` is also available. + +[NOTE] +==== +The `AsyncRestTemplate` also supports non-blocking interactions. The main difference +is it can't support non-blocking streaming, like for example +https://dev.twitter.com/streaming/overview[Twitter one], because fundamentally it's +still based and relies on `InputStream` and `OutputStream`. +==== + [[web-reactive-http-body]] -=== Reading and writing HTTP body +=== Encoding and Decoding the Request and Response Body The `spring-core` module provides reactive `Encoder` and `Decoder` contracts that enable the serialization of a `Flux` of bytes to and from typed objects. The `spring-web` module adds JSON (Jackson) and XML (JAXB) implementations for use in web applications as well as others for SSE streaming and zero-copy file transfer. -Whether you use the annotation-based or functional programming model, the request body -provided can be for example one of the following ways: +For example the request body can be one of the following way and it will be decoded +automatically in both the annotation and the functional programming models: -* `Account account` -- the account is deserialized without -blocking before the controller is invoked. -* `Mono account` -- the controller can use the `Mono` -to declare logic to be executed after the account is deserialized. +* `Account account` -- the account is deserialized without blocking before the controller is invoked. +* `Mono account` -- the controller can use the `Mono` to declare logic to be executed after the account is deserialized. * `Single account` -- same as with `Mono` but using RxJava * `Flux accounts` -- input streaming scenario. * `Observable accounts` -- input streaming with RxJava. -Similarly, the response body can be in any one of the following ways: +The response body can be one of the following: * `Mono` -- serialize without blocking the given Account when the `Mono` completes. * `Single` -- same but using RxJava. @@ -212,11 +215,54 @@ Similarly, the response body can be in any one of the following ways: * `Flowable` -- same but using RxJava 2 `Flowable` type. * `Flux` -- SSE streaming. * `Mono` -- request handling completes when the `Mono` completes. -* `Account` -- serialize without blocking the given Account; -implies a synchronous, non-blocking controller method. +* `Account` -- serialize without blocking the given Account; implies a synchronous, non-blocking controller method. * `void` -- specific to the annotation-based programming model, request handling completes when the method returns; implies a synchronous, non-blocking controller method. +[[web-reactive-websocket-support]] +=== Reactive WebSocket Support + +The Spring Framework 5 `spring-web-reactive` module includes reactive WebSocket +client and server support. Both client and server are supported on the Java WebSocket API +(JSR-356), Jetty, Undertow, Reactor Netty, and RxNetty. + +On the server side, declare a `WebSocketHandlerAdapter` and then simply add +mappings to `WebSocketHandler`-based endpoints: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +@Bean +public HandlerMapping webSocketMapping() { + Map map = new HashMap<>(); + map.put("/foo", new FooWebSocketHandler()); + map.put("/bar", new BarWebSocketHandler()); + + SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); + mapping.setUrlMap(map); + return mapping; +} + +@Bean +public WebSocketHandlerAdapter handlerAdapter() { + return new WebSocketHandlerAdapter(); +} +---- + +On the client side create a `WebSocketClient` for one of the supported libraries +listed above: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +WebSocketClient client = new ReactorNettyWebSocketClient(); +client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000); +---- + + + + + [[web-reactive-getting-started]] == Getting Started @@ -226,18 +272,22 @@ when the method returns; implies a synchronous, non-blocking controller method. The https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[Spring Boot Web Reactive starter] -available via http://start.spring.io is the fastest way to get started if you want to use -the annotation-based programming model. It does all that's necessary so you can start -writing `@Controller` classes. By default it runs on Tomcat but the dependencies can -be changed as usual with Spring Boot to switch to a different runtime. - -There is no Spring Boot Starter available yet for the functional programming model, so for -this one use the manual bootstraping method described bellow. +available via http://start.spring.io is the fastest way to get started. +It does all that's necessary so you to start writing `@Controller` classes +just like with Spring MVC. Simply go to http://start.spring.io, choose +version 2.0.0.BUILD-SNAPSHOT, and type reactive in the dependencies box. +By default the starter runs with Tomcat but the dependencies can be changed as usual with Spring Boot to switch to a different runtime. +See the +https://github.com/bclozel/spring-boot-web-reactive#spring-boot-web-reactive-starter[starter] +page for more details and instruction + +There is no Spring Boot Starter for the functional programming model yet but +it's very easy to try it out. See the next section on "Manual Bootstrapping". [[web-reactive-getting-started-manual]] === Manual Bootstrapping -This section outlines the steps to get up and running without Spring Boot. +This section outlines the steps to get up and running manually. For dependencies start with `spring-web-reactive` and `spring-context`. Then add `jackson-databind` and `io.netty:netty-buffer` @@ -250,7 +300,7 @@ Lastly add the dependencies for one of the supported runtimes: * RxNetty -- `io.reactivex:rxnetty-common` and `io.reactivex:rxnetty-http` * Undertow -- `io.undertow:undertow-core` -For the **annotation-based programming model**, bootstrap code start with: +For the **annotation-based programming model** bootstrap with: [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -260,15 +310,16 @@ HttpHandler handler = DispatcherHandler.toHttpHandler(context); // (2) The above loads default Spring Web Reactive config (1), then creates a `DispatcherHandler`, the main class driving request processing (2), and adapts -it to `HttpHandler`, the lowest level Spring abstraction for reactive HTTP request handling. +it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling. -For the **functional programming model**, bootstrap code start with: +For the **functional programming model** bootstrap as follows: [source,java,indent=0] [subs="verbatim,quotes"] ---- ApplicationContext context = new AnnotationConfigApplicationContext(); // (1) context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2) context.registerBean(BarBean.class); // (3) + HttpHandler handler = WebHttpHandlerBuilder .webHandler(RouterFunctions.toHttpHandler(...)) .applicationContext(context) @@ -279,7 +330,7 @@ The above creates an `AnnotationConfigApplicationContext` instance (1) that can of the new functional bean registration API (2) to register beans using a Java 8 `Supplier` or just by specifying its class (3). The `HttpHandler` is created using `WebHttpHandlerBuilder` (4). -**Common to both** annotation and functional flavors, an `HttpHandler` can then be installed in each supported runtime: +The `HttpHandler` can then be installed in one of the supported runtimes: [source,java,indent=0] [subs="verbatim,quotes"] @@ -290,10 +341,7 @@ HttpServlet servlet = new ServletHttpHandlerAdapter(handler); // Reactor Netty ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); -HttpServer.create(host, port) - .newHandler(adapter) - .doOnNext(c -> System.out.println("Server listening on " + c.address())).block() - .onClose().block(); +HttpServer.create(host, port).newHandler(adapter).onClose().block(); // RxNetty RxNettyHttpHandlerAdapter adapter = new RxNettyHttpHandlerAdapter(handler); @@ -308,10 +356,12 @@ server.start(); [NOTE] ==== -For Servlet runtimes you can use the `AbstractAnnotationConfigDispatcherHandlerInitializer`, -which as a `WebApplicationInitializer` is auto-detected by Servlet containers -and it registers for you the `ServletHttpHandlerAdapter` shown above. -Only implement one method to point to your Spring Java configuration classes. +For Servlet containers especially with WAR deployment you can use the +`AbstractAnnotationConfigDispatcherHandlerInitializer` which as a +`WebApplicationInitializer` and is auto-detected by Servlet containers. +It takes care of registering the `ServletHttpHandlerAdapter` as shown above. +You will need to implement one abstract method in order to point to your +Spring configuration. ==== [[web-reactive-getting-started-examples]]