搶先一步
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 將在單獨的執行緒中調用 Callable
,並借助 TaskExecutor
。這是之前的程式碼片段:
// 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,客戶端可以使用該 ID 查詢結果。
在其他需要結果的情況下,我們需要將處理與 Servlet 容器執行緒分離,否則我們將耗盡其執行緒池。Servlet 3 提供了這樣的支援,Servlet (或 Spring MVC 控制器) 可以指示在 Servlet 容器執行緒退出後應保持響應開啟。
為了實現這一點,Servlet 3 Web 應用程式可以調用 request.startAsync()
並使用返回的 AsyncContext
從其他單獨的執行緒繼續寫入響應。同時,從客戶端的角度來看,請求仍然看起來像任何其他 HTTP 請求-響應互動。它只是需要更長的時間才能完成。以下是事件的順序:
request.startAsync()
,保存 AsyncContext
,然後返回AsyncContext
來完成響應Servlet 非同步支援當然還有很多內容。您可以找到各種 範例和文章,但以上總結了您需要知道的基本、最小的概念。下一篇文章涵蓋了非同步請求處理的第二個動機——瀏覽器需要即時接收信息更新。