Doc improvements related to HTTP streaming

Issue: SPR-16494
This commit is contained in:
Rossen Stoyanchev 2018-03-07 17:07:09 -05:00
parent 568c93457a
commit eb96ff2943
2 changed files with 66 additions and 32 deletions

View File

@ -521,7 +521,6 @@ To configure or customize the readers and writers to use applications will typic
`ClientCodecConfigurer` or `ServerCodecConfigurer`.
[[webflux-codecs-jackson]]
==== Jackson
@ -550,6 +549,24 @@ serialized JSON) and are rendered as-is by the `CharSequenceEncoder`. If you wan
provide a `Mono<List<String>>` instead.
[[webflux-codecs-streaming]]
==== HTTP Streaming
[.small]#<<web.adoc#mvc-ann-async-http-streaming,Same in Spring MVC>>#
When a multi-value, reactive type such as `Flux` is used for response rendering, it may
be collected to a `List` and rendered as a whole (e.g. JSON array), or it may be treated
as an infinite stream with each item flushed immediately. The determination for which is
which is made based on content negotiation and the selected media type which may imply a
streaming format (e.g. "text/event-stream", "application/stream+json"), or not
(e.g. "application/json").
When streaming to the HTTP response, regardless of the media type (e.g. text/event-stream,
application/stream+json), it is important to send data periodically, since the write would
fail if the client has disconnected. The send could take the form of an empty
(comment-only) SSE event, or any other data that the other side would have to interpret as
a heartbeat and ignore.
[[webflux-dispatcher-handler]]
@ -1908,10 +1925,11 @@ than a meta-annotation marked with `@Controller` and `@ResponseBody`.
`@ResponseBody` supports reactive types which means you can return Reactor or RxJava
types and have the asynchronous values they produce rendered to the response.
For additional details on JSON rendering see <<webflux-codecs-jackson-json>>.
For additional details, see <<webflux-codecs-streaming>> and
<<webflux-codecs-jackson,JSON rendering>>.
`@ResponseBody` methods can be combined with JSON serialization views.
See <<mvc-ann-jackson>> for details.
See <<webflux-ann-jackson>> for details.
You can use the <<webflux-config-message-codecs>> option of the <<webflux-config>> to
configure or customize message writing.

View File

@ -3234,39 +3234,36 @@ See the Javadoc of `DeferredResult` for more details. `Callable` can be substitu
[[mvc-ann-async-vs-webflux]]
==== Compared to WebFlux
The Servlet API was originally built for sequential processing, i.e. making a single pass
through the Filter-Servlet chain. The asynchronous request processing feature added in
Servlet 3.0 allows applications to exit the Filter-Servlet chain but leave the response
open, therefore breaking this thread-per-request model.
The Servlet API was originally built for making a single pass through the Filter-Servlet
chain. Asynchronous request processing, added in Servlet 3.0, allows applications to exit
the Filter-Servlet chain but leave the response open for further processing. The Spring MVC
async support is built around that mechanism. When a controller returns a `DeferredResult`,
the Filter-Servlet chain is exited and the Servlet container thread is released. Later when
the `DeferredResult` is set, an ASYNC dispatch (to the same URL) is made during which the
controller is mapped again but rather than invoking it, the `DeferredResult` value is used
(as if the controller returned it) to resume processing.
Spring MVC async support is built around that model. When a controller returns a
`DeferredResult`, the Filter-Servlet chain is exited and the Servlet container thread is
released. Later when the `DeferredResult` is set, an ASYNC dispatch (to the same URL) is
made during which the controller is mapped again but not invoked. Instead the
`DeferredResult` value is used to resume processing.
By contrast Spring WebFlux is neither built on the Servlet API, nor does it need such an
asynchronous request processing feature because it is asynchronous by design. Asynchronous
handling is built into all framework contracts and is intrinsically supported through ::
stages of request processing.
Spring WebFlux is not aware of the Servlet API nor does it such an asynchronous request
processing feature because it is asynchronous by design. It processes each request in
stages (continuations) rather than making a single pass through the callstack on a single
thread. That means asynchronous handling is built into all framework contracts and is
therefore intrinsically supported at all stages of request processing.
Essentially both Spring MVC and Spring WebFlux support asynchronous and
<<mvc-ann-async-reactive-types>> for return values from controller methods. Spring MVC
even supports streaming, including reactive back pressure, however individual writes to
the response remain blocking (performed in a separate thread) and that is one major
difference with WebFlux which relies on non-blocking I/O.
From a programming model perspective, both Spring MVC and Spring WebFlux support
asynchronous and <<mvc-ann-async-reactive-types>> as return values in controller methods.
Spring MVC even supports streaming, including reactive back pressure. However individual
writes to the response remain blocking (and performed on a separate thread) unlike WebFlux
that relies on non-blocking I/O and does not need an extra thread for each write.
Another fundamental difference is that Spring MVC does not support asynchronous or
reactive types in controller method arguments, e.g. `@RequestBody`, `@RequestPart`, and
others, nor does it have any explicit support for asynchronous and reactive types as
model attributes, all of which Spring WebFlux does support.
model attributes. Spring WebFlux does support all that.
[[mvc-ann-async-http-streaming]]
=== HTTP Streaming
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>>#
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>>#
`DeferredResult` and `Callable` can be used for a single asynchronous return value.
What if you want to produce multiple asynchronous values and have those written to the
@ -3377,7 +3374,7 @@ customize the status and headers of the response.
[[mvc-ann-async-reactive-types]]
=== Reactive types
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>>#
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>>#
Spring MVC supports use of reactive client libraries in a controller. This includes the
`WebClient` from `spring-webflux` and others such as Spring Data reactive data
@ -3402,12 +3399,31 @@ Spring MVC supports Reactor and RxJava through the
`spring-core` which allows it to adapt from multiple reactive libraries.
====
When streaming to the response with a reactive type, Spring MVC performs (blocking)
writes to the response through the
through the <<mvc-ann-async-configuration-spring-mvc,configured>> MVC `TaskExecutor`.
By default this is a `SyncTaskExecutor` and not suitable for production.
https://jira.spring.io/browse/SPR-16203[SPR-16203] will provide better defaults.
In the mean time please configure the executor through the MVC config.
When streaming to the response via reactive types, Spring MVC supports reactive back
pressure, but still needs to use blocking I/O to perform actual writes. This is done
through the <<mvc-ann-async-configuration-spring-mvc,configured>> MVC `TaskExecutor` on
a separate thread in order to avoid blocking the upstream source (e.g. a `Flux` returned
from the `WebClient`). By default a `SyncTaskExecutor` is used which is not suitable for
production. https://jira.spring.io/browse/SPR-16203[SPR-16203] will provide better
defaults in Spring Framework 5.1. In the mean time please configure the executor through
the <<mvc-ann-async-configuration-spring-mvc,MVC config>>.
[[mvc-ann-async-disconnects]]
=== Disconnects
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>>#
The Servlet API does not provide any notification when a remote client goes away.
Therefore while streaming to the response, whether via <<mvc-ann-async-sse,SseEmitter>> or
<<mvc-ann-async-reactive-types,reactive types>, it is important to send data periodically,
since the write would fail if the client has disconnected. The send could take the form
of an empty (comment-only) SSE event, or any other data that the other side would have to
to interpret as a heartbeat and ignore.
Alternatively consider using web messaging solutions such as
<<websocket-stomp,STOMP over WebSocket>> or WebSocket with <<websocket-fallback,SockJS>>
that have a built-in heartbeat mechanism.