From 4b861aeae60952bb94e486e7d97e723eca048f0c Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 22 Jan 2018 22:42:59 -0500 Subject: [PATCH] Reference docs: update async request content Issue: SPR-16203 --- src/docs/asciidoc/web/webmvc.adoc | 314 ++++++++++++++---------------- 1 file changed, 150 insertions(+), 164 deletions(-) diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 8808c6b6b2..482e8a4c92 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -3153,19 +3153,47 @@ or in a JSP: [[mvc-ann-async]] == Async Requests +Spring MVC has an extensive integration with the Servlet 3.0 asynchronous request +processing. <> and <> +provide basic support for producing return values asynchronously. Controllers can produce +<> including +<> and <>. Controllers can +use reactive clients and return <> +to Spring MVC for response handling. -[[mvc-ann-async-processing]] -=== Processing -Spring MVC 3.2 introduced Servlet 3 based asynchronous request processing. Instead of -returning a value, as usual, a controller method can now return a -`java.util.concurrent.Callable` and produce the return value from a Spring MVC managed thread. -Meanwhile the main Servlet container thread is exited and released and allowed to process other -requests. Spring MVC invokes the `Callable` in a separate thread with the help of a -`TaskExecutor` and when the `Callable` returns, the request is dispatched back to the -Servlet container to resume processing using the value returned by the `Callable`. Here -is an example of such a controller method: +[[mvc-ann-async-deferredresult]] +=== `DeferredResult` + +Once the asynchronous request processing feature is +<> in the Servlet container, controller methods can +wrap any supported controller method return value with `DeferredResult`: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @GetMapping("/quotes") + @ResponseBody + public DeferredResult quotes() { + DeferredResult deferredResult = new DeferredResult(); + // Save the deferredResult somewhere.. + return deferredResult; + } + + // From some other thread... + deferredResult.setResult(data); +---- + +The controller can produce the return value asynchronously, from a different thread, for +example in response to an external event (JMS message), a scheduled task, or other. + + + +[[mvc-ann-async-callable]] +=== `Callable` + +A controller may also wrap any supported return value with `java.util.concurrent.Callable`: [source,java,indent=0] [subs="verbatim,quotes"] @@ -3183,30 +3211,15 @@ is an example of such a controller method: } ---- -Another option is for the controller method to return an instance of `DeferredResult`. In this -case the return value will also be produced from any thread, i.e. one that -is not managed by Spring MVC. For example the result may be produced in response to some -external event such as a JMS message, a scheduled task, and so on. Here is an example -of such a controller method: +The return value will then be obtained by executing the the given task through the +<> `TaskExecutor`. -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @RequestMapping("/quotes") - @ResponseBody - public DeferredResult quotes() { - DeferredResult deferredResult = new DeferredResult(); - // Save the deferredResult somewhere.. - return deferredResult; - } - // In some other thread... - deferredResult.setResult(data); ----- -This may be difficult to understand without any knowledge of the Servlet 3.0 -asynchronous request processing features. It would certainly help to read up -on that. Here are a few basic facts about the underlying mechanism: +[[mvc-ann-async-processing]] +=== Processing + +Here is a very concise overview of Servlet asynchronous request processing: * A `ServletRequest` can be put in asynchronous mode by calling `request.startAsync()`. The main effect of doing so is that the Servlet, as well as any Filters, can exit but @@ -3219,98 +3232,83 @@ on that. Here are a few basic facts about the underlying mechanism: be used to distinguish between processing the initial request, an async dispatch, a forward, and other dispatcher types. -With the above in mind, the following is the sequence of events for async request -processing with a `Callable`: - -* Controller returns a `Callable`. -* Spring MVC starts asynchronous processing and submits the `Callable` to - a `TaskExecutor` for processing in a separate thread. -* The `DispatcherServlet` and all Filter's exit the Servlet container thread - but the response remains open. -* The `Callable` produces a result and Spring MVC dispatches the request back - to the Servlet container to resume processing. -* The `DispatcherServlet` is invoked again and processing resumes with the - asynchronously produced result from the `Callable`. - -The sequence for `DeferredResult` is very similar except it's up to the -application to produce the asynchronous result from any thread: +`DeferredResult` processing: * Controller returns a `DeferredResult` and saves it in some in-memory queue or list where it can be accessed. -* Spring MVC starts async processing. -* The `DispatcherServlet` and all configured Filter's exit the request +* Spring MVC calls `request.startAsync()`. +* Meanwhile the `DispatcherServlet` and all configured Filter's exit the request processing thread but the response remains open. * The application sets the `DeferredResult` from some thread and Spring MVC dispatches the request back to the Servlet container. * The `DispatcherServlet` is invoked again and processing resumes with the - asynchronously produced result. + asynchronously produced return value. + +`Callable` processing: + +* Controller returns a `Callable`. +* Spring MVC calls `request.startAsync()` and submits the `Callable` to + a `TaskExecutor` for processing in a separate thread. +* Meanwhile the `DispatcherServlet` and all Filter's exit the Servlet container thread + but the response remains open. +* Eventually the `Callable` produces a result and Spring MVC dispatches the request back + to the Servlet container to complete processing. +* The `DispatcherServlet` is invoked again and processing resumes with the + asynchronously produced return value from the `Callable`. -For further background on the motivation for async request processing and -when or why to use it please read -https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[this -blog post series]. +For further background and context you can also read +https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[the +blog posts] that introduced asynchronous request processing support in Spring MVC 3.2. [[mvc-ann-async-exceptions]] === Exception handling -What happens if a `Callable` returned from a controller method raises an -Exception while being executed? The short answer is the same as what happens -when a controller method raises an exception. It goes through the regular -exception handling mechanism. The longer explanation is that when a `Callable` -raises an Exception Spring MVC dispatches to the Servlet container with -the `Exception` as the result and that leads to resume request processing -with the `Exception` instead of a controller method return value. -When using a `DeferredResult` you have a choice whether to call -`setResult` or `setErrorResult` with an `Exception` instance. +When using a `DeferredResult` you can choose whether to call `setResult` or +`setErrorResult` with an exception. In both cases Spring MVC dispatches the request back +to the Servlet container to complete processing. It is then treated either as if the +controller method returned the given value, or as if it produced the given exception. +The exception then goes through the regular exception handling mechanism, e.g. invoking +`@ExceptionHandler` methods. + +When using `Callable`, similar processing logic follows. The main difference being that +the result is returned from the `Callable` or an exception is raised by it. [[mvc-ann-async-interception]] -=== Async interceptors +=== Interception -A `HandlerInterceptor` can also implement `AsyncHandlerInterceptor` in order -to implement the `afterConcurrentHandlingStarted` callback, which is called -instead of `postHandle` and `afterCompletion` when asynchronous processing -starts. +``HandlerInterceptor``'s can also be `AsyncHandlerInterceptor` in order to receive the +`afterConcurrentHandlingStarted` callback on the initial request that starts asynchronous +processing instead of `postHandle` and `afterCompletion`. -A `HandlerInterceptor` can also register a `CallableProcessingInterceptor` -or a `DeferredResultProcessingInterceptor` in order to integrate more -deeply with the lifecycle of an asynchronous request and for example -handle a timeout event. See the Javadoc of `AsyncHandlerInterceptor` +``HandlerInterceptor``'s can also register a `CallableProcessingInterceptor` +or a `DeferredResultProcessingInterceptor` in order to integrate more deeply with the +lifecycle of an asynchronous request for example to handle a timeout event. See +{api-spring-framework}/web/servlet/AsyncHandlerInterceptor.html[AsyncHandlerInterceptor] for more details. -The `DeferredResult` type also provides methods such as `onTimeout(Runnable)` -and `onCompletion(Runnable)`. See the Javadoc of `DeferredResult` for more -details. - -When using a `Callable` you can wrap it with an instance of `WebAsyncTask` -which also provides registration methods for timeout and completion. +`DeferredResult` provides `onTimeout(Runnable)` and `onCompletion(Runnable)` callbacks. +See the Javadoc of `DeferredResult` for more details. `Callable` can be substituted for +`WebAsyncTask` that exposes additional methods for timeout and completion callbacks. [[mvc-ann-async-http-streaming]] === Streaming response -A controller method can use `DeferredResult` and `Callable` to produce its -return value asynchronously and that can be used to implement techniques such as -http://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[long polling] -where the server can push an event to the client as soon as possible. - -What if you wanted to push multiple events on a single HTTP response? -This is a technique related to "Long Polling" that is known as "HTTP Streaming". -Spring MVC makes this possible through the `ResponseBodyEmitter` return value -type which can be used to send multiple Objects, instead of one as is normally -the case with `@ResponseBody`, where each Object sent is written to the -response with an <>. - -Here is an example of that: +What if you wanted to push multiple events on a single HTTP response? The +`ResponseBodyEmitter` return value can be used to stream multiple Objects, where each +Object sent is serialized with an +<> and written to the +response. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping("/events") + @GetMapping("/events") public ResponseBodyEmitter handle() { ResponseBodyEmitter emitter = new ResponseBodyEmitter(); // Save the emitter somewhere.. @@ -3327,53 +3325,58 @@ Here is an example of that: emitter.complete(); ---- -Note that `ResponseBodyEmitter` can also be used as the body in a -`ResponseEntity` in order to customize the status and headers of -the response. +`ResponseBodyEmitter` can also be used as the body in a `ResponseEntity` allowing you to +customize the status and headers of the response. [[mvc-ann-async-sse]] === Server-Sent Events -`SseEmitter` is a subclass of `ResponseBodyEmitter` providing support for -http://www.w3.org/TR/eventsource/[Server-Sent Events]. -Server-sent events is a just another variation on the same "HTTP Streaming" -technique except events pushed from the server are formatted according to -the W3C Server-Sent Events specification. +`SseEmitter` is a sub-class of `ResponseBodyEmitter` that provides support for +http://www.w3.org/TR/eventsource/[Server-Sent Events] where events sent from the server +are formatted according to the W3C SSE specification. In order to produce an SSE +stream from a controller simply return `SseEmitter`: -Server-Sent Events can be used for their intended purpose, that is to push -events from the server to clients. It is quite easy to do in Spring MVC and -requires simply returning a value of type `SseEmitter`. +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE) + public SseEmitter handle() { + SseEmitter emitter = new SseEmitter(); + // Save the emitter somewhere.. + return emitter; + } + + // In some other thread + emitter.send("Hello once"); + + // and again later on + emitter.send("Hello again"); + + // and done at some point + emitter.complete(); +---- -Note however that Internet Explorer does not support Server-Sent Events and -that for more advanced web application messaging scenarios such as online games, -collaboration, financial applicatinos, and others it's better to consider -Spring's WebSocket support that includes SockJS-style WebSocket emulation -falling back to a very wide range of browsers (including Internet Explorer) -and also higher-level messaging patterns for interacting with clients through -a publish-subscribe model within a more messaging-centric architecture. -For further background on this see -http://blog.pivotal.io/pivotal/products/websocket-architecture-in-spring-4-0[the following blog post]. +While SSE is the main option for streaming into browsers, note that Internet Explorer +does not support Server-Sent Events. Consider using Spring's +<> with +<> transports (including SSE) that target +a wide range of browsers. [[mvc-ann-async-output-stream]] === Streaming raw data -`ResponseBodyEmitter` allows sending events by writing Objects to the -response through an <>. -This is probably the most common case, for example when writing JSON data. -However sometimes it is useful to bypass message conversion and write directly to the -response `OutputStream` for example for a file download. This can be done with the help -of the `StreamingResponseBody` return value type. - -Here is an example of that: +Sometimes it is useful to bypass message conversion and stream directly to the response +`OutputStream` for example for a file download. Use the of the `StreamingResponseBody` +return value type to do that: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping("/download") + @GetMapping("/download") public StreamingResponseBody handle() { return new StreamingResponseBody() { @Override @@ -3384,61 +3387,44 @@ Here is an example of that: } ---- -Note that `StreamingResponseBody` can also be used as the body in a -`ResponseEntity` in order to customize the status and headers of -the response. +`StreamingResponseBody` can be used as the body in a `ResponseEntity` allowing you to +customize the status and headers of the response. [[mvc-ann-async-reactive-types]] === Reactive return values -If using the reactive `WebClient` from `spring-webflux`, or another client, or -a data store with reactive support, you can return reactive types directly from -Spring MVC controller methods. +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 +repositories. In such scenarios it is convenient to be able to return reactive types +from the controller method . -Spring MVC adapts transparently to the reactive library in use with proper translation -of cardinality -- i.e. how many values are expected. This is done with the help of the -{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from -`spring-core` which provides pluggable support for reactive and async types. The registry -has built-in support for RxJava but others can be registered. - -Return values are handled as follows: - -* If the return type has single-value stream semantics such as Reactor `Mono` or -RxJava `Single` it is adapted and equivalent to using `DeferredResult`. -* If the return type has multi-value stream semantics such as Reactor `Flux` or -RxJava `Observable` / `Flowable` and if the media type indicates streaming, e.g. -"application/stream+json" or "text/event-stream", it is adapted and equivalent to -using `ResponseBodyEmitter` or `SseEmitter`. You can also return -`Flux` or `Observable`. -* If the return type has multi-value stream semantics but the media type does not -imply streaming, e.g. "application/json", it is adapted and equivalent to using -`DeferredResult>`, e.g. JSON array. - -Reactive libraries are detected and adapted to a Reactive Streams `Publisher` -through Spring's pluggable `ReactiveAdapterRegistry` which by default supports -Reactor 3, RxJava 2, and RxJava 1. Note that for RxJava 1 you will need to add -https://github.com/ReactiveX/RxJavaReactiveStreams["io.reactivex:rxjava-reactive-streams"] -to the classpath. - -A common assumption with reactive libraries is to not block the processing thread. -The `WebClient` with Reactor Netty for example is based on event-loop style -handling using a small, fixed number of threads and those must not be blocked -when writing to the `ServletResponseOutputStream`. Reactive libraries have -operators for that but Spring MVC automatically writes asynchronously so you -don't need to use them. The underlying `TaskExecutor` for this must be configured -through the MVC Java config and the MVC namespace as described in the following -section which by default is a `SyncTaskExecutor` and hence not suitable for -production use. +Reactive return values are handled as follows: -[NOTE] +* A single-value promise is adapted to and similar to using `DeferredResult`. Examples +include `Mono` (Reactor) or `Single` (RxJava). +* A multi-value stream, with a streaming media type such as `"application/stream+json"` +or `"text/event-stream"`, is adapted to and similar to using `ResponseBodyEmitter` or +`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava). +Applications can also return `Flux` or `Observable`. +* A multi-value stream, with any other media type (e.g. "application/json"), is adapted +to and similar to using `DeferredResult>`. + +[TIP] ==== -Unlike Spring MVC, Spring WebFlux is built on a non-blocking, reactive foundation -and uses the Servlet 3.1 non-blocking I/O that's also based on event loop style -processing and hence does not require a thread to absorb the effect of blocking. +Spring MVC supports Reactor and RxJava through the +{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from +`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 `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. + [[mvc-ann-async-configuration]]