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

相关文章
相关标签/搜索