异步管理

140 阅读2分钟
/根据当前请求与响应创建异步请求

AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);

//设置异步请求的超时时间为当前组件中配置的超时时间
asyncWebRequest.setTimeout(this.asyncRequestTimeout);


//获取异步管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

//设置异步管理器的任务执行器为当前组件中配置的任务执行器
asyncManager.setTaskExecutor(this.taskExecutor);

//设置异步管理器管理的异步请求实例
asyncManager.setAsyncWebRequest(asyncWebRequest);

//向异步管理器中注册Callable拦截器,Callable拦截器来自于当前组件的配置
asyncManager.registerCallableInterceptors(this.callableInterceptors);

//向异步管理器中注册DeferredResult拦截器,DeferredResult拦截器来自于当前组件的配置
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);


//如果异步管理器中管理的异步请求已经有了结果
if (asyncManager.hasConcurrentResult()) {

//则获取异步执行结果
   Object result = asyncManager.getConcurrentResult();

//获取异步系欸过上下文中维护的ModelAndView容器
   mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];

//清理异步管理器的结果,减少内存占用
   asyncManager.clearConcurrentResult();
   LogFormatUtils.traceDebug(logger, traceOn -> {
      String formatted = LogFormatUtils.formatValue(result, !traceOn);
      return "Resume with async result [" + formatted + "]";
   });

//把invocableMethod包装为返回result的方法
   invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

//调用处理器方法并处理返回值

invocableMethod.invokeAndHandle(webRequest, mavContainer);

//如果处理完成后,开启了异步请求并在处理中,说明返回值为一个异步结果,直接返回null,等待异步结果返回,再执行上面的获取异步结果逻辑
if (asyncManager.isConcurrentHandlingStarted()) {
   return null;
}

异步请求会请求dodispatch主流程方法两次:

第一次是正常的流程,asyncManager.hasConcurrentResult()为false,然后走到invocableMethod.invokeAndHandle(webRequest, mavContainer)来;

第二次,asyncManager.hasConcurrentResult()为true,然后带着result进入到invocableMethod.invokeAndHandle(webRequest, mavContainer)方法来;

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   setResponseStatus(webRequest);

   if (returnValue == null) {
      if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
         disableContentCachingIfNecessary(webRequest);
         mavContainer.setRequestHandled(true);
         return;
      }
   }
   else if (StringUtils.hasText(getResponseStatusReason())) {
      mavContainer.setRequestHandled(true);
      return;
   }

   mavContainer.setRequestHandled(false);
   Assert.state(this.returnValueHandlers != null, "No return value handlers");
   try {
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   }
   catch (Exception ex) {
      if (logger.isTraceEnabled()) {
         logger.trace(formatErrorForReturnValue(returnValue), ex);
      }
      throw ex;
   }
}
这个方法包括了参数解析和返回值解析这两个最为核心的部分,我们后边讲;

@GetMapping(value = {"/get2"})
@ResponseBody
public DeferredResult<String> say(){
    DeferredResult<String> result = new DeferredResult<>();
       new Timer().schedule(new TimerTask() {
        public void run() {
            result.setResult("2222");
        }
    }, 3000);
    System.out.println("1111");
    return result;
}

上面这个是一个关于异步管理的一个get请求

第一行的Object returnValue,执行了上面的get请求,可以看到是不为null的。就会走到下面

this.returnValueHandlers.handleReturnValue(
      returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
   if (handler == null) {
      throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
   }
   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);就是通过返回值的类型来选择返回值参数解析的;

image.png

可以看到第三个,就是我们get请求对应的返回值解析处理方法了;
然后我们来到handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);就是说利用刚才得到的返回值解析器处理返回值的时候到了;

来到DeferredResultMethodReturnValueHandler的handleReturnValue方法:

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

   if (returnValue == null) {
      mavContainer.setRequestHandled(true);
      return;
   }

   DeferredResult<?> result;

   if (returnValue instanceof DeferredResult) {
      result = (DeferredResult<?>) returnValue;
   }
   else if (returnValue instanceof ListenableFuture) {
      result = adaptListenableFuture((ListenableFuture<?>) returnValue);
   }
   else if (returnValue instanceof CompletionStage) {
      result = adaptCompletionStage((CompletionStage<?>) returnValue);
   }
   else {
      // Should not happen...
      throw new IllegalStateException("Unexpected return value type: " + returnValue);
   }
   WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
}

最主要是最底下的 WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
这里边就是执行第二次dodispatch的关键所在:
拉到最底下找到setConcurrentResultAndDispatch(result);

private void setConcurrentResultAndDispatch(Object result) {
   synchronized (WebAsyncManager.this) {
      if (this.concurrentResult != RESULT_NONE) {
         return;
      }
      this.concurrentResult = result;
      this.errorHandlingInProgress = (result instanceof Throwable);
   }

   if (this.asyncWebRequest.isAsyncComplete()) {
      if (logger.isDebugEnabled()) {
         logger.debug("Async result set but request already complete: " + formatRequestUri());\
      }
      return;
   }

   if (logger.isDebugEnabled()) {
      boolean isError = result instanceof Throwable;
      logger.debug("Async " + (isError ? "error" : "result set") + ", dispatch to " + formatRequestUri());
   }
   this.asyncWebRequest.dispatch();
}

可以看到这里这个this.asyncWebRequest.dispatch();
我们再追下去,这个类其实已经是到tomcat的jar包下的文件了,我还没研究过tomcat,所以就走个过场,知道调用的流程就好了

image.png

image.png

image.png

点击最底下的action,

image.png

case ASYNC_DISPATCH: {
    if (asyncStateMachine.asyncDispatch()) {
        processSocketEvent(SocketEvent.OPEN_READ, true);
    }
    break;
}
会来到这个ASYNC_DISPATCH这里来,

image.png