Skip to content

Commit eb96ff2

Browse files
committed
Doc improvements related to HTTP streaming
Issue: SPR-16494
1 parent 568c934 commit eb96ff2

File tree

2 files changed

+68
-34
lines changed

2 files changed

+68
-34
lines changed

src/docs/asciidoc/web/webflux.adoc

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,6 @@ To configure or customize the readers and writers to use applications will typic
521521
`ClientCodecConfigurer` or `ServerCodecConfigurer`.
522522

523523

524-
525524
[[webflux-codecs-jackson]]
526525
==== Jackson
527526

@@ -550,6 +549,24 @@ serialized JSON) and are rendered as-is by the `CharSequenceEncoder`. If you wan
550549
provide a `Mono<List<String>>` instead.
551550

552551

552+
[[webflux-codecs-streaming]]
553+
==== HTTP Streaming
554+
[.small]#<<web.adoc#mvc-ann-async-http-streaming,Same in Spring MVC>>#
555+
556+
When a multi-value, reactive type such as `Flux` is used for response rendering, it may
557+
be collected to a `List` and rendered as a whole (e.g. JSON array), or it may be treated
558+
as an infinite stream with each item flushed immediately. The determination for which is
559+
which is made based on content negotiation and the selected media type which may imply a
560+
streaming format (e.g. "text/event-stream", "application/stream+json"), or not
561+
(e.g. "application/json").
562+
563+
When streaming to the HTTP response, regardless of the media type (e.g. text/event-stream,
564+
application/stream+json), it is important to send data periodically, since the write would
565+
fail if the client has disconnected. The send could take the form of an empty
566+
(comment-only) SSE event, or any other data that the other side would have to interpret as
567+
a heartbeat and ignore.
568+
569+
553570

554571

555572
[[webflux-dispatcher-handler]]
@@ -1908,10 +1925,11 @@ than a meta-annotation marked with `@Controller` and `@ResponseBody`.
19081925

19091926
`@ResponseBody` supports reactive types which means you can return Reactor or RxJava
19101927
types and have the asynchronous values they produce rendered to the response.
1911-
For additional details on JSON rendering see <<webflux-codecs-jackson-json>>.
1928+
For additional details, see <<webflux-codecs-streaming>> and
1929+
<<webflux-codecs-jackson,JSON rendering>>.
19121930

19131931
`@ResponseBody` methods can be combined with JSON serialization views.
1914-
See <<mvc-ann-jackson>> for details.
1932+
See <<webflux-ann-jackson>> for details.
19151933

19161934
You can use the <<webflux-config-message-codecs>> option of the <<webflux-config>> to
19171935
configure or customize message writing.

src/docs/asciidoc/web/webmvc.adoc

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3234,39 +3234,36 @@ See the Javadoc of `DeferredResult` for more details. `Callable` can be substitu
32343234
[[mvc-ann-async-vs-webflux]]
32353235
==== Compared to WebFlux
32363236

3237-
The Servlet API was originally built for sequential processing, i.e. making a single pass
3238-
through the Filter-Servlet chain. The asynchronous request processing feature added in
3239-
Servlet 3.0 allows applications to exit the Filter-Servlet chain but leave the response
3240-
open, therefore breaking this thread-per-request model.
3241-
3242-
Spring MVC async support is built around that model. When a controller returns a
3243-
`DeferredResult`, the Filter-Servlet chain is exited and the Servlet container thread is
3244-
released. Later when the `DeferredResult` is set, an ASYNC dispatch (to the same URL) is
3245-
made during which the controller is mapped again but not invoked. Instead the
3246-
`DeferredResult` value is used to resume processing.
3247-
3248-
Spring WebFlux is not aware of the Servlet API nor does it such an asynchronous request
3249-
processing feature because it is asynchronous by design. It processes each request in
3250-
stages (continuations) rather than making a single pass through the callstack on a single
3251-
thread. That means asynchronous handling is built into all framework contracts and is
3252-
therefore intrinsically supported at all stages of request processing.
3253-
3254-
Essentially both Spring MVC and Spring WebFlux support asynchronous and
3255-
<<mvc-ann-async-reactive-types>> for return values from controller methods. Spring MVC
3256-
even supports streaming, including reactive back pressure, however individual writes to
3257-
the response remain blocking (performed in a separate thread) and that is one major
3258-
difference with WebFlux which relies on non-blocking I/O.
3237+
The Servlet API was originally built for making a single pass through the Filter-Servlet
3238+
chain. Asynchronous request processing, added in Servlet 3.0, allows applications to exit
3239+
the Filter-Servlet chain but leave the response open for further processing. The Spring MVC
3240+
async support is built around that mechanism. When a controller returns a `DeferredResult`,
3241+
the Filter-Servlet chain is exited and the Servlet container thread is released. Later when
3242+
the `DeferredResult` is set, an ASYNC dispatch (to the same URL) is made during which the
3243+
controller is mapped again but rather than invoking it, the `DeferredResult` value is used
3244+
(as if the controller returned it) to resume processing.
3245+
3246+
By contrast Spring WebFlux is neither built on the Servlet API, nor does it need such an
3247+
asynchronous request processing feature because it is asynchronous by design. Asynchronous
3248+
handling is built into all framework contracts and is intrinsically supported through ::
3249+
stages of request processing.
3250+
3251+
From a programming model perspective, both Spring MVC and Spring WebFlux support
3252+
asynchronous and <<mvc-ann-async-reactive-types>> as return values in controller methods.
3253+
Spring MVC even supports streaming, including reactive back pressure. However individual
3254+
writes to the response remain blocking (and performed on a separate thread) unlike WebFlux
3255+
that relies on non-blocking I/O and does not need an extra thread for each write.
32593256

32603257
Another fundamental difference is that Spring MVC does not support asynchronous or
32613258
reactive types in controller method arguments, e.g. `@RequestBody`, `@RequestPart`, and
32623259
others, nor does it have any explicit support for asynchronous and reactive types as
3263-
model attributes, all of which Spring WebFlux does support.
3260+
model attributes. Spring WebFlux does support all that.
32643261

32653262

32663263

32673264
[[mvc-ann-async-http-streaming]]
32683265
=== HTTP Streaming
3269-
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>>#
3266+
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>>#
32703267

32713268
`DeferredResult` and `Callable` can be used for a single asynchronous return value.
32723269
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.
33773374

33783375
[[mvc-ann-async-reactive-types]]
33793376
=== Reactive types
3380-
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>>#
3377+
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>>#
33813378

33823379
Spring MVC supports use of reactive client libraries in a controller. This includes the
33833380
`WebClient` from `spring-webflux` and others such as Spring Data reactive data
@@ -3402,12 +3399,31 @@ Spring MVC supports Reactor and RxJava through the
34023399
`spring-core` which allows it to adapt from multiple reactive libraries.
34033400
====
34043401

3405-
When streaming to the response with a reactive type, Spring MVC performs (blocking)
3406-
writes to the response through the
3407-
through the <<mvc-ann-async-configuration-spring-mvc,configured>> MVC `TaskExecutor`.
3408-
By default this is a `SyncTaskExecutor` and not suitable for production.
3409-
https://jira.spring.io/browse/SPR-16203[SPR-16203] will provide better defaults.
3410-
In the mean time please configure the executor through the MVC config.
3402+
When streaming to the response via reactive types, Spring MVC supports reactive back
3403+
pressure, but still needs to use blocking I/O to perform actual writes. This is done
3404+
through the <<mvc-ann-async-configuration-spring-mvc,configured>> MVC `TaskExecutor` on
3405+
a separate thread in order to avoid blocking the upstream source (e.g. a `Flux` returned
3406+
from the `WebClient`). By default a `SyncTaskExecutor` is used which is not suitable for
3407+
production. https://jira.spring.io/browse/SPR-16203[SPR-16203] will provide better
3408+
defaults in Spring Framework 5.1. In the mean time please configure the executor through
3409+
the <<mvc-ann-async-configuration-spring-mvc,MVC config>>.
3410+
3411+
3412+
3413+
[[mvc-ann-async-disconnects]]
3414+
=== Disconnects
3415+
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>>#
3416+
3417+
The Servlet API does not provide any notification when a remote client goes away.
3418+
Therefore while streaming to the response, whether via <<mvc-ann-async-sse,SseEmitter>> or
3419+
<<mvc-ann-async-reactive-types,reactive types>, it is important to send data periodically,
3420+
since the write would fail if the client has disconnected. The send could take the form
3421+
of an empty (comment-only) SSE event, or any other data that the other side would have to
3422+
to interpret as a heartbeat and ignore.
3423+
3424+
Alternatively consider using web messaging solutions such as
3425+
<<websocket-stomp,STOMP over WebSocket>> or WebSocket with <<websocket-fallback,SockJS>>
3426+
that have a built-in heartbeat mechanism.
34113427

34123428

34133429

0 commit comments

Comments
 (0)