Tomcat请求处理源码分析(四)

一、长连接

  在Http请求头中,Connection: keep-alive 代表长连接。在tomcat io线程读写时,是否保持长连接的方法如下:

// doRun() method logic in SocketProcessor
if (handshake == 0) {
    SocketState state = SocketState.OPEN;
    // Process the request from this socket
    if (event == null) {
        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
    } else {
        state = getHandler().process(socketWrapper, event);
    }
    if (state == SocketState.CLOSED) {
        poller.cancelledKey(key, socketWrapper);
    }
} else if (handshake == -1 ) {
    poller.cancelledKey(key, socketWrapper);
}

handshake 为 -1 表明SSL握手有问题,调用 poller.cancelledKey() 方法来关闭原始 socket 连接。

如果握手正常,则调用ConnectionHandler的 process() 方法会间接的调用Http11Processor 的 service() 方法返回SocketState,根据SocketState的值决定是否关闭socket连接,如果不为CLOSED则保持长连接。

核心代码如下:

    public SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
        
        ···
        
        // Flags
        keepAlive = true;
        openSocket = false;
        readComplete = true;
        boolean keptAlive = false;
        SendfileState sendfileState = SendfileState.DONE;

        while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
                sendfileState == SendfileState.DONE && !endpoint.isPaused()) {

            ···
            //maxKeepAliveRequests在Http11Processor初始化的时候被赋值100
            if (maxKeepAliveRequests == 1) {
                keepAlive = false;
            } else if (maxKeepAliveRequests > 0 &&
                    //keepAliveLeft的默认值也是100,每次有新的连接会-1
                    socketWrapper.decrementKeepAlive() <= 0) {
                keepAlive = false;
            }
            ···
            //把keepAlive赋值给openSocket
            sendfileState = processSendfile(socketWrapper);
        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

        if (getErrorState().isError() || (endpoint.isPaused() && !isAsync())) {
            return SocketState.CLOSED;
        } else if (isAsync()) {
            return SocketState.LONG;
        } else if (isUpgrade()) {
            return SocketState.UPGRADING;
        } else {
            if (sendfileState == SendfileState.PENDING) {
                return SocketState.SENDFILE;
            } else {
                //true则保持长连接
                if (openSocket) {
                    if (readComplete) {
                        return SocketState.OPEN;
                    } else {
                        return SocketState.LONG;
                    }
                } else {
                    //false关闭连接
                    return SocketState.CLOSED;
                }
            }
        }
    }
    
    private SendfileState processSendfile(SocketWrapperBase<?> socketWrapper) {
        openSocket = keepAlive;
        //Other logic in processSendfile method
        return result;
    }

由源码可知,默认的长连接数最大为100,即 server 端每个长连接可以支持 100 个请求,超过就会关闭连接。

除了以上在 tomcat io 线程中决定是否使用长连接之外,poller 线程也可以决定是否使用长连接。在 poller 的循环 run() 方法里会调用 timeout() 方法来决定是否关闭连接,核心逻辑如下:

if ((socketWrapper.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ || (socketWrapper.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
  boolean isTimedOut = false;
  boolean readTimeout = false;
  boolean writeTimeout = false;
  // Check for read timeout
  if ((socketWrapper.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
      long delta = now - socketWrapper.getLastRead();
      long timeout = socketWrapper.getReadTimeout();
      isTimedOut = timeout > 0 && delta > timeout;
      readTimeout = true;
  }
  // Check for write timeout
  if (!isTimedOut && (socketWrapper.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
      long delta = now - socketWrapper.getLastWrite();
      long timeout = socketWrapper.getWriteTimeout();
      isTimedOut = timeout > 0 && delta > timeout;
      writeTimeout = true;
  }
  if (isTimedOut) {
      key.interestOps(0);
      // Avoid duplicate timeout calls
          socketWrapper.interestOps(0);
          socketWrapper.setError(new SocketTimeoutException());
          if (readTimeout && socketWrapper.readOperation != null) {
              if (!socketWrapper.readOperation.process()) {
                  cancelledKey(key, socketWrapper);
              }
          } else if (writeTimeout && socketWrapper.writeOperation != null) {
              if (!socketWrapper.writeOperation.process()) {
                  cancelledKey(key, socketWrapper);
              }
          } else if (!processSocket(socketWrapper, SocketEvent.ERROR, true)) {
              cancelledKey(key, socketWrapper);
          }
      }
  }

该方法会判断是否有读写超时,读写超时时间由 NioSocketWrapper 实例的 getReadTimeout() 和 getWriteTimeout() 决定,默认都为 1 分钟。
NioSocketWrapper 实例会有 getLastRead() 和 getLastWrite() 方法记录最近一次读写时间,根据上面超时时间判断是否超时(1分钟内没有读写操作)。
根据上述如果读写超时,一般情况会走 processSocket(socketWrapper,SocketEvent.ERROR, true) 调用,传递 SocketEvent.ERROR 作为 socket 事件。而对于 error 事件处理也是关闭 socket 。即使上面调用不成功也会调用 cancelledKey() 方法来关闭 socket ,从而不保持长连接。

二、总结

 

 

 

 

 

 

参考链接:https://blog.csdn.net/weixin_46073333/article/details/110359292

posted @ 2021-10-27 17:30  上官兰夏  阅读(169)  评论(0编辑  收藏  举报