当前位置 主页 > 服务器问题 > Linux/apache问题 >

    详解Tomcat是如何实现异步Servlet的(3)

    栏目:Linux/apache问题 时间:2019-11-08 10:56

    isAsync() 判断,就会进入 dispatch(status) ,最终会调用 CoyoteAdapterasyncDispatch 方法

    public boolean asyncDispatch(org.apache.coyote.Request req, org.apache.coyote.Response res,
       SocketEvent status) throws Exception {
      //省略部分代码
      Request request = (Request) req.getNote(ADAPTER_NOTES);
      Response response = (Response) res.getNote(ADAPTER_NOTES);
      boolean success = true;
      AsyncContextImpl asyncConImpl = request.getAsyncContextInternal();
      try {
       if (!request.isAsync()) {
        response.setSuspended(false);
       }
    
       if (status==SocketEvent.TIMEOUT) {
        if (!asyncConImpl.timeout()) {
         asyncConImpl.setErrorState(null, false);
        }
       } else if (status==SocketEvent.ERROR) {
        
       }
    
       if (!request.isAsyncDispatching() && request.isAsync()) {
        WriteListener writeListener = res.getWriteListener();
        ReadListener readListener = req.getReadListener();
        if (writeListener != null && status == SocketEvent.OPEN_WRITE) {
         ClassLoader oldCL = null;
         try {
          oldCL = request.getContext().bind(false, null);
          res.onWritePossible();//这里执行浏览器响应,写入数据
          if (request.isFinished() && req.sendAllDataReadEvent() &&
            readListener != null) {
           readListener.onAllDataRead();
          }
         } catch (Throwable t) {
          
         } finally {
          request.getContext().unbind(false, oldCL);
         }
        } 
        }
       }
       //这里判断异步正在进行,说明这不是一个完成方法的回调,是一个正常异步请求,继续调用容器。
       if (request.isAsyncDispatching()) {
        connector.getService().getContainer().getPipeline().getFirst().invoke(
          request, response);
        Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
        if (t != null) {
         asyncConImpl.setErrorState(t, true);
        }
       }
       //注意,这里,如果超时或者出错,request.isAsync()会返回false,这里是为了尽快的输出错误给客户端。
       if (!request.isAsync()) {
        //这里也是输出逻辑
        request.finishRequest();
        response.finishResponse();
       }
       //销毁request和response
       if (!success || !request.isAsync()) {
        updateWrapperErrorCount(request, response);
        request.recycle();
        response.recycle();
       }
      }
      return success;
     }

    上面的代码就是 ctx.complete() 执行最终的方法了(当然省略了很多细节),完成了数据的输出,最终输出到浏览器。

    这里有同学可能会说,我知道异步执行完后,调用 ctx.complete() 会输出到浏览器,但是,第一次doGet请求执行完成后,Tomcat是怎么知道不用返回到客户端的呢?关键代码在 CoyoteAdapter 中的 service 方法,部分代码如下:

     postParseSuccess = postParseRequest(req, request, res, response);
       //省略部分代码
       if (postParseSuccess) {
        request.setAsyncSupported(
          connector.getService().getContainer().getPipeline().isAsyncSupported());
        connector.getService().getContainer().getPipeline().getFirst().invoke(
          request, response);
       }
       if (request.isAsync()) {
        async = true;
        } else {
        //输出数据到客户端
        request.finishRequest();
        response.finishResponse();
       if (!async) {
        updateWrapperErrorCount(request, response);
        //销毁request和response
        request.recycle();
        response.recycle();
       }