Browse Source

More documentation updates

pull/1279/head
Rossen Stoyanchev 9 years ago
parent
commit
6bf12812f1
  1. 250
      src/asciidoc/web-reactive.adoc

250
src/asciidoc/web-reactive.adoc

@ -1,6 +1,7 @@ @@ -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 @@ -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. @@ -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. @@ -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<DataBuffer>` 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<DataBuffer>`, rather than
`InputStream` and `OutputStream`, with reactive backpressure.
REST-style JSON and XML serialization and deserialization is supported on top
as a `Flux<Object>`, 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 { @@ -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 @@ -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<DataBuffer>` 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<DataBuffer>` 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<Void> request = ClientRequest.GET("http://example.com/accounts/{id}", 1L)
.accept(MediaType.APPLICATION_JSON).build();
ClientRequest<Void> request = ClientRequest
.GET("http://example.com/accounts/{id}", 1L)
.accept(APPLICATION_JSON)
.build();
Mono<Account> account = this.webClient
.exchange(request)
.then(response -> response.bodyToMono(Account.class));
Mono<Account> 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> 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> account` -- the controller can use the `Mono` to declare logic to be executed after the account is deserialized.
* `Single<Account> account` -- same as with `Mono` but using RxJava
* `Flux<Account> accounts` -- input streaming scenario.
* `Observable<Account> 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<Account>` -- serialize without blocking the given Account when the `Mono` completes.
* `Single<Account>` -- same but using RxJava.
@ -212,11 +215,54 @@ Similarly, the response body can be in any one of the following ways: @@ -212,11 +215,54 @@ Similarly, the response body can be in any one of the following ways:
* `Flowable<Account>` -- same but using RxJava 2 `Flowable` type.
* `Flux<ServerSentEvent>` -- SSE streaming.
* `Mono<Void>` -- 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<String, WebSocketHandler> 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. @@ -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: @@ -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) @@ -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 @@ -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); @@ -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(); @@ -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]]

Loading…
Cancel
Save