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.

运行时

  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);是你业务代码执行的步骤。

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.

运行时流程有点绕,没功夫画时序图。

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这段请求中处理。

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发起之前。

HTTP Streaming

正是请求被异步化,从而使得能long polling,即HTTP Streaming。

可以用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.

    原文作者:重构地球
    原文地址: https://segmentfault.com/a/1190000013284975
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞