遥遥领先
VMware 提供培训和认证,以加速您的进步。
了解更多上次更新于 2012 年 11 月 5 日(Spring MVC 3.2 RC1)
概览
Spring MVC 3.2 引入了基于 Servlet 3 的异步请求处理。这是介绍这项新功能并提供理解其用法和原因的上下文的系列博文中的第一篇。
早期版本的主要目的是征求反馈。自从 3.2 M1 版本发布后,我们在这里和 JIRA 中收到了大量反馈。感谢每一位尝试并评论的人!已经进行了许多更改,并且仍然有时间提供更多反馈!
概览
从编程模型的角度来看,新功能看起来出奇地简单。现在,控制器方法可以返回一个 java.util.concurrent.Callable
以异步完成处理。然后,Spring MVC 将在单独的线程中借助 TaskExecutor
调用 Callable
。以下是之前的代码片段
// Before
@RequestMapping(method=RequestMethod.POST)
public String processUpload(final MultipartFile file) {
// ...
return "someView";
}
// After
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public Object call() throws Exception {
// ...
return "someView";
}
};
}
控制器方法还可以返回一个 DeferredResult
(Spring MVC 3.2 中的新类型),以便在 Spring MVC 不知道的线程中完成处理。例如,响应 JMS 或 AMQP 消息、Redis 通知等等。以下是另一个代码片段
@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Add deferredResult to a Queue or a Map...
return deferredResult;
}
// In some other thread...
deferredResult.setResult(data);
// Remove deferredResult from the Queue or Map
以上示例会引发许多问题,我们将在后续文章中详细介绍。现在,我将首先提供有关这些功能的一些背景信息。
Web 应用程序中异步的动机
Web 应用程序中异步的最基本动机是处理需要更长时间才能完成的请求。可能是一个慢速数据库查询,对外部 REST API 的调用或其他一些 I/O 绑定操作。此类较长的请求可能会迅速耗尽 Servlet 容器线程池并影响可伸缩性。
在某些情况下,您可以立即返回给客户端,而后台作业完成处理。例如,发送电子邮件、启动数据库作业等代表了即发即弃场景,可以使用 Spring 的 @Async 支持或通过将事件发布到 Spring Integration 通道来处理,然后返回客户端可以用来查询结果的确认 ID。
在需要结果的其他情况下,我们需要将处理与 Servlet 容器线程分离,否则我们将耗尽其线程池。Servlet 3 提供了这样的支持,其中 Servlet(或 Spring MVC 控制器)可以指示在 Servlet 容器线程退出后应保持响应打开。
为了实现这一点,Servlet 3 Web 应用程序可以调用 request.startAsync()
并使用返回的AsyncContext
从一些其他的单独线程继续写入响应。与此同时,从客户端的角度来看,该请求仍然看起来像任何其他 HTTP 请求-响应交互。它只是需要更长的时间才能完成。以下是事件序列
request.startAsync()
,保存 AsyncContext
并返回AsyncContext
来完成响应当然,Servlet 异步支持还有很多内容。你可以找到 各种 示例 和 说明,但以上总结了你需要了解的基本、最少概念。下一篇文章介绍了异步请求处理的第二个动机 -- 浏览器需要实时接收信息更新。