直播帶貨原始碼,非同步處理中會處理兩次請求

云豹科技-苏凌霄發表於2024-03-16

直播帶貨原始碼,非同步處理中會處理兩次請求

在這裡插入圖片描述
從序列圖上可以看到SpringMVC在處理非同步請求時,DispatcherServlet會處理兩次請求

具體來看HandlerAdapter的處理過程

//根據HandlerMethod解析引數 並完成過程呼叫得到一個ModelAndView
private ModelAndView invokeHandleMethod(HttpServletRequest request,
                 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  try {
    //對@InitBinder的處理 主要是聚合了@InitBinder的所有處理方法
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    //@ModelAttribute的處理
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    //對HandlerMethod的裝飾,主要是增加了引數解析和返回值轉化的功能
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    //提供對引數解析的支援
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    //提供對返回值解析的支援
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    //提供對@InitBinder處理的支援
    invocableMethod.setDataBinderFactory(binderFactory);
    //TODO 尚不明功能
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
    //可以看做handler()過程的上下文
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
==========================非同步處理分割線=============    
    //AsyncWebRequest內部持有AsyncContext 可以透過其開啟非同步任務
    AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    //非同步處理Manager
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    //設定非同步執行執行緒池
    asyncManager.setTaskExecutor(this.taskExecutor);
    //提供對非同步處理的支援
    asyncManager.setAsyncWebRequest(asyncWebRequest);
    //非同步呼叫攔截器
    asyncManager.registerCallableInterceptors(this.callableInterceptors);
    asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    //如果非同步處理完成
    if (asyncManager.hasConcurrentResult()) {
      //獲取非同步執行結果
      Object result = asyncManager.getConcurrentResult();
      mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
      asyncManager.clearConcurrentResult();
      ...
      //替換invocableMethod(原先非同步處理的方法返回值是Callable現在直接返回結果)
      invocableMethod = invocableMethod.wrapConcurrentResult(result);
    }
    //對invocableMethod進行引數解析,過程呼叫,返回值轉化
    //並將結果存到mavContainer中
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    //如果非同步處理正在執行(已經開始,尚未結束) 立刻返回
    //同時DispatcherServlet也直接返回 等待AsyncContext.dispatch()呼叫再次進入doDispatch()方法
    if (asyncManager.isConcurrentHandlingStarted()) {
      return null;
    }
    //從mavContainer撈出結果
    return getModelAndView(mavContainer, modelFactory, webRequest);
  }
  finally {
    webRequest.requestCompleted();
  }
}

這裡非同步的處理 針對兩次請求有兩種處理

第一次請求: 開始非同步請求

//AsyncWebRequest內部持有AsyncContext 可以透過其開啟非同步任務
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
//非同步處理Manager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//設定非同步執行執行緒池
asyncManager.setTaskExecutor(this.taskExecutor);
//提供對非同步處理的支援
asyncManager.setAsyncWebRequest(asyncWebRequest);
//非同步呼叫攔截器
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
//對invocableMethod進行引數解析,過程呼叫(呼叫AsyncWebRequest.startAsync()執行非同步過程),返回值轉化
//並將結果存到mavContainer中
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//如果非同步處理正在執行(已經開始,尚未結束) 立刻返回
//同時DispatcherServlet也直接返回 
return null;

#org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  ...
  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  if (asyncManager.isConcurrentHandlingStarted()) {
    return;
  }
  ...
}
//等待AsyncWebRequest.dispatch()被呼叫 然後再次進入doDispatch()方法

其實可以看到 在invokeHandleMethod()的處理過程除了最後直接返回null,前面的處理都和正常流程是一樣的
在SpringMVC中非同步方法無非只是返回值是個Callable()而已 ,所以其引數解析過程和正常流程是一樣的,區別在於返回值解析過程
來看返回值處理過程

public class CallableMethodReturnValueHandler implements AsyncHandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return Callable.class.isAssignableFrom(returnType.getParameterType());
    }
    @Override
    public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) {
        return (returnValue != null && returnValue instanceof Callable);
    }
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        if (returnValue == null) {
            mavContainer.setRequestHandled(true);
            return;
        }
        //將Callable物件丟給非同步執行器執行
        Callable<?> callable = (Callable<?>) returnValue;
        WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer);
    }
}
#org.springframework.web.context.request.async.WebAsyncManager
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext) throws Exception {
    //超時控制,攔截器配置
    ...
    //呼叫Request.startAsync()得到AsyncContext物件
    startAsyncProcessing(processingContext);
    try {
        this.taskExecutor.submit(new Runnable() {
            @Override
            public void run() {
                Object result = null;
                try {
                    interceptorChain.applyPreProcess(asyncWebRequest, callable);
                    result = callable.call();
                }
                ...
                finally {
                    result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result);
                }
                //通知非同步執行結束 呼叫dispatch()
                setConcurrentResultAndDispatch(result);
            }
        });
    }
    catch (RejectedExecutionException ex) {
        //異常處理
        Object result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, ex);
        setConcurrentResultAndDispatch(result);
        throw ex;
    }
}
 private void setConcurrentResultAndDispatch(Object result) {
    ...
    //呼叫AsyncContext.dispatch() 通知servlet容器再起發起請求
    this.asyncWebRequest.dispatch();
}

第二次請求: 非同步執行完成

 ```java
   //AsyncWebRequest內部持有AsyncContext 可以透過其開啟非同步任務
   AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
   asyncWebRequest.setTimeout(this.asyncRequestTimeout);
   //非同步處理Manager
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   //設定非同步執行執行緒池
   asyncManager.setTaskExecutor(this.taskExecutor);
   //提供對非同步處理的支援
   asyncManager.setAsyncWebRequest(asyncWebRequest);
   //非同步呼叫攔截器
   asyncManager.registerCallableInterceptors(this.callableInterceptors);
   asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
   //非同步處理完成 獲取非同步執行結果
   Object result = asyncManager.getConcurrentResult();
   mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
   asyncManager.clearConcurrentResult();
   //!!!替換invocableMethod(原先HandlerMethod中返回值是Callable現在直接返回結果,無需進行引數解析)
   invocableMethod = invocableMethod.wrapConcurrentResult(result);
   //對invocableMethod進行引數解析,過程呼叫,返回值轉化
   //並將結果存到mavContainer中
   invocableMethod.invokeAndHandle(webRequest, mavContainer);
   //從mavContainer撈出結果
   return getModelAndView(mavContainer, modelFactory, webRequest);

以上就是直播帶貨原始碼,非同步處理中會處理兩次請求, 更多內容歡迎關注之後的文章

相關文章