SpringMVC異步處理的 5 種方式示例詳解
前段時間研究了下 diamond 的原理,其中有個重要的知識點是長連接的實現(xiàn),用到了 servlet 的異步處理。異步處理最大的好處是可以提高并發(fā)量,不阻塞當前線程。其實 Spring MVC 也支持了異步處理,本文記錄下相關的技術點。
異步處理 demo
如果要啟用異步返回,需要開啟 @EnableAsync。如下的代碼中,使用 DeferredResult 進行異步處理。
請求進來后,首先創(chuàng)建 DeferredResult 對象,設置超時時間為 60 秒。然后指定DeferredResult 在異步完成和等待超時時的回調(diào)。同步的處理只需要創(chuàng)建異步任何,然后返回DeferredResult 即可。這樣 Spring MVC 處理完此次請求后,不會立即返回 response 給客戶端,會一直等待DeferredResult 處理完成。如果DeferredResult 沒有在 60 秒內(nèi)處理完成,就會觸發(fā)超時,然后返回 response 給客戶端。
@RequestMapping(value = "/async/demo") public DeferredResult<String> async(){ // 創(chuàng)建 DeferredResult,設置超時時間 60s DeferredResult<String> deferredResult = new DeferredResult<>((long)60 * 1000); String uuid = UUID.randomUUID().toString(); Runnable callback = () -> manager.remove(deferredResult, uuid); // 設置完成和超時的回調(diào) deferredResult.onCompletion(callback); deferredResult.onTimeout(callback); // 創(chuàng)建異步任務 manager.addAsyncTask(deferredResult, uuid); // 同步返回 DeferredResult return deferredResult; }
對于異步任務來說,需要持有DeferredResult 對象。在異步處理結束時,需要手動調(diào)用DeferredResult.setResult完成輸出。調(diào)用setResult 時,數(shù)據(jù)輸出寫到客戶端,然后觸發(fā)異步完成事件執(zhí)行回調(diào)。
task.getDeferredResult().setResult(ConfigJsonUtils.toJsonString(map));
使用 DeferredResult 進行異步處理
DeferredResult 這個類代表延遲結果。DeferredResult 可以用在異步任務中,其他線程能夠獲取DeferredResult并設置DeferredResult 的返回數(shù)據(jù)。通??梢允褂镁€程池、隊列等配合DeferredResult 實現(xiàn)異步處理。
根據(jù)官方描述,Spring MVC 處理流程如下:
- 把 controller 返回的 DeferredResult 保存在內(nèi)存隊列或集合當中;
- Spring MVC 調(diào)用 request.startAsync(),開啟異步;
- DispatcherServlet 和所有的 Filter 退出當前請求線程;
- 業(yè)務應用在異步線程中設置 DeferredResult 的返回值, Spring MVC 會再次發(fā)送請求;
- DispatcherServlet 再次被調(diào)用,并使用 DeferredResult 的返回值;
使用 Callable 進行異步處理
使用 Callable 進行異步處理與 DeferredResult 類似。不同的是,Callable 會交給系統(tǒng)指定的 TaskExecutor 執(zhí)行。
根據(jù)官方描述,Spring MVC 處理流程如下:
- controller 返回 Callable ;
- Spring MVC 調(diào)用 request.startAsync(),開啟異步,提交 Callable 到一個任務線程池 ;
- DispatcherServlet 和所有的 Filter 退出當前請求線程;
- 業(yè)務應用在異步線程中 返回值, Spring MVC 會再次發(fā)送請求;
- DispatcherServlet 再次被調(diào)用,并使用 Callable 的返回值;
@RequestMapping(value = "/async/demo") public Callable<String> async(){ Callable<String> callable = () -> String.valueOf(System.currentTimeMillis()); // 同步返回 return callable; }
使用 ListenableFuture 進行異步處理
ListenableFuture 作為返回值,與DeferredResult 類似。也需要使用者自行處理異步線程,但不支持超時、完成回調(diào),需要自行處理。
@RequestMapping(value = "/async/demo") public ListenableFuture<String> async(){ ListenableFutureTask<String> ListenableFuture= new ListenableFutureTask<>(() -> { return String.valueOf(System.currentTimeMillis()); }); Executors.newSingleThreadExecutor().submit(ListenableFuture); return ListenableFuture; }
使用 ResponseBodyEmitter 進行異步處理
DeferredResult 和 Callable 都只能返回一個異步值。如果需要返回多個對象,就要使用 ResponseBodyEmitter。返回的每個對象都會被 HttpMessageConverter 處理并寫回輸出流。如果希望設置更多返回數(shù)據(jù),如 header、status 等,可以把 ResponseBodyEmitter 作為 ResponseEntity 的實體數(shù)據(jù)返回。
@RequestMapping("/async/responseBodyEmitter") public ResponseBodyEmitter responseBodyEmitter(){ ResponseBodyEmitter responseBodyEmitter=new ResponseBodyEmitter(); Executors.newSingleThreadExecutor().submit(() -> { try { responseBodyEmitter.send("demo"); responseBodyEmitter.send("test"); responseBodyEmitter.complete(); } catch (Exception ignore) {} }); return responseBodyEmitter; }
使用 StreamingResponseBody 進行異步處理
如果希望跳過返回值的自動轉(zhuǎn)換,直接把輸出流寫入OutputStream,可以使用 StreamingResponseBody。也可以作為 ResponseEntity 的實體數(shù)據(jù)返回。
@RequestMapping("/async/streamingResponseBody") public StreamingResponseBody streamingResponseBody(){ StreamingResponseBody streamingResponseBody = outputStream -> { Executors.newSingleThreadExecutor().submit(() -> { try { outputStream.write("<html>streamingResponseBody</html>".getBytes()); } catch (IOException ignore) {} }); }; return streamingResponseBody; }
各種處理方式的對比
|
數(shù)據(jù)轉(zhuǎn)換 |
回調(diào) |
線程池 |
|
DeferredResult |
1 次 |
有 |
完成、超時 |
自行處理 |
Callable |
1 次 |
有 |
無 |
系統(tǒng)處理 |
ListenableFuture |
1 次 |
有 |
無 |
自行處理 |
ResponseBodyEmitter |
多次 |
有 |
無 |
自行處理 |
StreamingResponseBody |
多次 |
無 |
無 |
自行處理 |
到此這篇關于SpringMVC異步處理的 5 種方式的文章就介紹到這了,更多相關SpringMVC異步處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot 統(tǒng)一公共返回類的實現(xiàn)
本文主要介紹了SpringBoot 統(tǒng)一公共返回類的實現(xiàn),配置后臺的統(tǒng)一公共返回類,這樣做目的是為了統(tǒng)一返回信息,文中示例代碼介紹的很詳細,感興趣的可以了解一下2022-01-01java開發(fā)AOP基礎JdkDynamicAopProxy
這篇文章主要為大家介紹了java開發(fā)AOP基礎JdkDynamicAopProxy源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07使用?EasyCode生成springboot+mybatis基礎程序的實現(xiàn)示例
本文主要介紹了使用?EasyCode生成springboot+mybatis基礎程序的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01基于自定義BufferedReader中的read和readLine方法
下面小編就為大家分享一篇基于自定義BufferedReader中的read和readLine方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12Java下SpringBoot創(chuàng)建定時任務詳解
這篇文章主要介紹了Java下SpringBoot創(chuàng)建定時任務詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07java中javamail發(fā)送帶附件的郵件實現(xiàn)方法
這篇文章主要介紹了java中javamail發(fā)送帶附件的郵件實現(xiàn)方法,較為詳細的分析了JavaMail發(fā)送郵件的用法,是非常實用的技巧,需要的朋友可以參考下2015-01-01SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式
在Web應用程序開發(fā)中,統(tǒng)一數(shù)據(jù)返回格式對于前后端分離項目尤為重要,本文就來介紹一下SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式,具有一定的參考價值,感興趣的可以了解一下2024-07-07解決mybatis 執(zhí)行mapper的方法時報空指針問題
這篇文章主要介紹了解決mybatis 執(zhí)行mapper的方法時報空指針問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07基于Failed?to?load?ApplicationContext異常的解決思路
這篇文章主要介紹了基于Failed?to?load?ApplicationContext異常的解決思路,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01