Spring - Asynchronous Request

用法

@GetMapping("/ddd")
public Callable<String> process() { 

    return () -> {
        Thread.sleep(1000L);
        return "call";
    };
}


@GetMapping("/ddd")
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
     
    return deferredResult;
}

// In some other thread...
deferredResult.setResult(data);

返回Callable和DeferredResult區別在於the return value of DeferredResult will also be produced from any thread, i.e. one that is not managed by Spring MVC.web

運行時

  1. 客戶端請求,DispatcherType:REQUEST 即與同步請求同樣
  2. Controller返回Callable
  3. HandlerMethodReturnValueHandlerComposite選出CallableMethodReturnValueHandler來處理Callable
  4. WebAsyncManager#startCallableProcessing -> #startAsyncProcessing -> StandardServletAsyncWebRequest#startAsync
  5. 因爲spring的StandardServletAsyncWebRequest內部託管J2EE容器的Request(該Request實現ServletRequest),因此調用ServletRequest#startAsync(ServletRequest, ServletResponse)生成AsyncContext託管於StandardServletAsyncWebRequest內部,該AsyncContext保存ServletRequest和ServletResponse,以便以後的DispatcherType:ASYNC請求能取出對應的ServletResponse
  6. WebAsyncManager#startCallableProcessing 調用完畢後,向taskExecutor線程池中提交任務來執行Callable#call,注意該任務最後一定會執行WebAsyncManager#setConcurrentResultAndDispatch,即執行StandardServletAsyncWebRequest#dispatch,就是執行前面生成的AsyncContext#dispatch
  7. DispatcherType:ASYNC 這是由AsyncContext#dispatch發起的。
  8. 注意這時RequestMappingHandlerAdapter#invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)中if (asyncManager.hasConcurrentResult())結果爲true,即將原來的ServletInvocableHandlerMethod替換成ConcurrentResultHandlerMethod,因此invocableMethod.invokeAndHandle(webRequest, mavContainer);直接返回taskExecutor線程池執行的結果。
  9. HandlerMethodReturnValueHandlerComposite選出你結果類型對應的ReturnValueHandler來處理你的結果。
  10. 返回客戶端。

提醒:RequestMappingHandlerAdapter#invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)中的invocableMethod.invokeAndHandle(webRequest, mavContainer);是你業務代碼執行的步驟。spring

The call to request.startAsync() returns AsyncContext which can be used for further control over async processing. For example it provides the method dispatch, that is similar to a forward from the Servlet API except it allows an application to resume request processing on a Servlet container thread.app

運行時流程有點繞,沒功夫畫時序圖。異步

Exception Handling

when a Callable raises an Exception Spring MVC dispatches to the Servlet container with the Exception as the result and that leads to resume request processing with the Exception instead of a controller method return value. When using a DeferredResult you have a choice whether to call setResult or setErrorResult with an Exception instance.
因此異步異常處理和同步相同,在DispatcherType:ASYNC這段請求中處理。async

Intercepting Async Requests

  • The DeferredResult type also provides methods such as onTimeout(Runnable) and onCompletion(Runnable).
  • When using a Callable you can wrap it with an instance of WebAsyncTask which also provides registration methods for timeout and completion.

AsyncHandlerInterceptor比HandlerInterceptor多#afterConcurrentHandlingStarted,調用時間即DispatcherType:ASYNC發起以前。ide

HTTP Streaming

正是請求被異步化,從而使得能long polling,即HTTP Streaming。spa

能夠用ResponseBodyEmitter來建立邏輯上的Streaming,注意ResponseBodyEmitter#send的內容都會經過對應的HttpMessageConverter來轉化:線程

@RequestMapping("/events")
public ResponseBodyEmitter handle() {
    ResponseBodyEmitter emitter = new ResponseBodyEmitter();
    // Save the emitter somewhere..
    return emitter;
}

// In some other thread
emitter.send("Hello once");

// and again later on
emitter.send("Hello again");

// and done at some point
emitter.complete();

固然 ResponseBodyEmitter can also be used as the body in a ResponseEntity in order to customize the status and headers of the response.code

相關文章
相關標籤/搜索