SpringMVC DeferedResult和servlet3.1 AsyncContext異步請求


 

先看一個簡單的示例:

 1 @RequestMapping("/getFuture")
 2 public Future<String> getFuture() {
 3     System.out.println(1);
 4     // 必須是ListenableFuture的子類,或者CompletionStage子類,或者是DeferredResult 不能是FutureTask
 5     // 如何返回CompletableFuture
 6     CompletableFuture<String> ftu2 = CompletableFuture.supplyAsync(() -> {
 7         try {
 8             // RequestContextHolder.setRequestAttributes(attributes); 
 9             // 使用ThreadLocal相關特性時,需要在外面先get
10             Thread.sleep(3000L);
11         } catch (InterruptedException e) {
12         } finally {
13             // RequestContextHolder.resetRequestAttributes();
14         }
15         return "THIS string";
16     }, es);
17 
18     //Future<String> future = executorService.submit(() -> { }); //返回值是FutureTask
19 
20     return ftu2;
21 }

 

 




客戶端發起請求
===> DispatcherServlet根據返回值類型查找對應的handlar AsyncHandlerMethodReturnValueHandler
【Callable -->
CallableMethodReturnValueHandler; DeferredResult,CallableFuture,ListenableFuture --> DeferredResultMethodReturnValueHandler】
===> Controller中返回Future對象給容器, 
org.springframework.web.context.request.async.WebAsyncManager#startDeferredResultProcessing處理Future結果的監聽

===>
sleep3秒之后,Future完成后調用asyncWebRequest.dispatch() 重新發給Container,DispatcherServlet找到Message的序列化器將結果輸出。DispatcherServlet.doService會進入兩次!!!

異步線程的主要功能是:
業務處理耗較長(上面的sleep3)時,可以先返回Future對象釋放Container的work線程, work線程可以接收更多的請求。等Future完成之后重新調用Container的另一個work線程,輸出response.

這里work線程可以是BIO中work線程, 也可以是NIO中work線程

不是所有的Future都能在異步線程中處理
https://www.cnblogs.com/dennyzhangdd/p/7010972.html


spring boot中配置container的work線程數量
server.tomcat.max-threads=5
server.tomcat.min-spare-threads=3
 
使用異步處理的優點:
增加系統吞吐量,對響應速度提高不大,可能還會降低
缺點:
線程關系復雜, 異步超時、異常日志攔截需要實現具體的Adapter接口,threadLocal不好清理

參考:《億級流量架構核心技術》http://jinnianshilongnian.iteye.com/blog/2245925
Servlet3.1 規范
    tomcat線程配置 https://www.cnblogs.com/kismetv/p/7806063.html


遺留問題: Future.get是什么時候調用的?
沒有直接調用get。 ListenerableFuture, CompletableFuture任務完成之后都會觸發回調

work1線程: 接收到請求---> DispatcherServlet--> return future -->WebAsyncManager#startDeferredResultProcessing
自定義的任務線程: CompletableFuture.postComplete之后調用 WebAsyncManager#setConcurrentResultAndDispatch
work2線程: dipatch ---> DispatcherServlet --> response.out


關於第二個任務線程
返回值是Callable時,調用WebAsyncManager#startCallableProcessing, 此時任務線程使用的是AsyncTaskExecutor
可以通過org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#configureAsyncSupport設置自定義的AsyncTaskExecutor

返回值是DeferredResult,ListenableFuture (Spring提供),CompletableFuture(JDK自帶)時,調用WebAsyncManager#startDeferredResultProcessing,此時任務線程池可以直接指定
@See CompletableFuture.supply(xxxTask, threadPool)

@RequestMapping("/callableTest")
public Callable<String> callableTest() {
    Callable<String> hello = () -> {
        // 此處不能設置線程池, WebAsyncManager中會使用AsyncSupportConfigurer的線程池
        System.out.println("===>" + Thread.currentThread().getName());
        return "HELLO";
    };
    return hello;
}

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM