这里是对 Servlet 异步请求 处理的一个非常简明的概述:
ServletRequest 可以通过调用
request.startAsync()
进入异步模式。这样做的主要效果是,Servlet(以及任何过滤器)可以退出,但响应仍然是开放的,以便以后完成处理。对
request.startAsync()
的调用返回 AsyncContext,你可以用它来进一步控制异步处理。例如,它提供了 dispatch 方法,它类似于Servlet API 中的 forward,只是它让应用程序在 Servlet 容器线程上恢复请求处理。
ServletRequest 提供了对当前 DispatcherType 的访问,你可以用它来区分处理初始请求、异步调度、转发和其他调度器类型。
DeferredResult 处理工作如下:
- 控制器返回一个 DeferredResult,并将其保存在一些可以被访问的内存队列或列表中。
- Spring MVC 调用
request.startAsync()
。 - 同时,DispatcherServlet 和所有配置的过滤器退出请求处理线程,但响应仍然开放。
- 应用程序从某个线程设置 DeferredResult,Spring MVC 将请求分派回 Servlet 容器。
- DispatcherServlet 被再次调用,处理过程以异步产生的返回值继续进行。
Callable 的处理工作如下:
- 控制器返回一个 Callable。
- Spring MVC 调用
request.startAsync()
并将 Callable 提交给 TaskExecutor,在一个单独的线程中进行处理。 - 同时,DispatcherServlet 和所有过滤器退出 Servlet 容器线程,但响应仍然是开放的。
- 最终,Callable 产生了一个结果,Spring MVC 将该请求分派回 Servlet 容器以完成处理。
- DispatcherServlet 被再次调用,处理过程会随着 Callable 异步产生的返回值而恢复。
要了解更多的背景和情况,你也可以阅读介绍 Spring MVC 3.2 中 异步请求处理支持的博文。
Exception Handling / 异常处理
当你使用一个 DeferredResult 时,你可以选择是否调用 setResult 或 setErrorResult 来处理异常。在这两种情况下,Spring MVC 都会将请求分派回 Servlet 容器以完成处理。然后,它被当作控制器方法返回给定值或产生给定的异常来处理。然后,该异常将通过常规的异常处理机制(例如,调用 @ExceptionHandler 方法)。
当你使用 Callable 时,类似的处理逻辑也会发生,主要的区别在于,结果是由 Callable 返回的,还是由它引发的异常。
Interception / 拦截器
HandlerInterceptor 实例可以是 AsyncHandlerInterceptor 类型,以接收启动异步处理的初始请求的 afterConcurrentHandlingStarted
回调(而不是 postHandle 和 afterCompletion)。
HandlerInterceptor 实现还可以注册一个 CallableProcessingInterceptor 或 DeferredResultProcessingInterceptor,以便更深入地与异步请求的生命周期相结合(例如,处理一个超时事件)。见 AsyncHandlerInterceptor 了解更多细节。
DeferredResult 提供 onTimeout(Runnable)
和 onCompletion(Runnable)
回调。更多细节见 DeferredResult 的 javadoc。Callable 可以被替换为 WebAsyncTask,它为超时和完成回调提供了额外的方法。
与 WebFlux 相比
Servlet API 最初是为在 Filter-Servlet 链中进行单次传递而构建的。在 Servlet 3.0 中加入的异步请求处理让应用程序可以退出 Filter-Servlet 链,但为进一步处理留下了响应。Spring MVC 的异步支持是围绕这一机制建立的。当控制器返回 DeferredResult 时,Filter-Servlet 链被退出,Servlet 容器线程被释放。后来,当 DeferredResult 被设置时,一个 ASYNC 调度(到相同的 URL)被进行,在此期间,控制器再次被映射,但不是调用它, DeferredResult 值被使用(就像控制器返回它一样),以恢复处理。
相比之下,Spring WebFlux 既没有建立在 Servlet API上,也不需要这样的异步请求处理功能,因为它在设计上就是异步的。异步处理被内置到所有的框架合约中,并通过请求处理的所有阶段得到内在的支持。
从编程模型的角度来看,Spring MVC 和 Spring WebFlux 都支持异步和 Reactive Types 作为控制器方法的返回值。Spring MVC 甚至支持 streaming,包括 reactive back pressure。然而,对响应的单独写入仍然是阻塞的(并且在一个单独的线程上执行),这与 WebFlux 不同,WebFlux 依赖于非阻塞的 I/O,并且不需要为每次写入增加一个线程。
另一个根本区别是,Spring MVC 不支持控制器方法参数中的异步或反应式类型(例如 @RequestBody、@RequestPart 等),也没有明确支持异步和反应式类型作为模型属性。Spring WebFlux 确实支持所有这些。