tomcat一段时间不操作oracle就关闭连接_Tomcat NIO(15)长连接

在上一篇文章里我们主要介绍了 tomcat nio 中 block poller 线程的阻塞与唤醒,当 tomcat io 线程读取请求实体数据不可读或者写入响应数据不可写的时候,就会注册事件到 block poller 线程中,并阻塞当前线程。block poller 线程负责注册并监测可读可写到原始 socket ,当可读可写的时候唤醒阻塞的线程,让其继续处理读写事件。在这里我们主要介绍 tomcat 中的长连接。 tomcat 数据的读写都在 io 线程中,根据以前文章 i o 线程序列图如下:

3bc43a9c7046fa4bfdf974002d706bd8.png

对于 SocketProcessor 类,决定是否保持长连接的核心代码如下:
// doRun() method logic in SocketProcessorif (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 表明握手有问题,调用 poller.cancelledKey() 方法来关闭原始 socket 不使用长连接。
  • 握手正常,取决 ConnectionHandler.process() 返回的 SocketState 。如果状态为 CLOSED 则直接关闭原始 socket ,否则将不关闭保持长连接。

  • 根据以前文章,ConnectionHandler 实例的 process() 方法会间接的调用Http11Processor 的 service() 方法来决定返回的 SocketState ,其核心逻辑如下:

    // service() method in Http11Processorif (openSocket) {      if (readComplete) {          return SocketState.OPEN;      } else {          return SocketState.LONG;      } } else {      return SocketState.CLOSED; }
  • 由上可知 SocketState 由 openSocket 全局变量决定,为 false 的时候关闭连接,为 true 的时候不关闭连接。

  • openSocket 全局变量又会在 Http11Processor 的 service() 方法中通过调用 processSendfile() 方法改变,逻辑如下:

    private SendfileState processSendfile(SocketWrapperBase> socketWrapper) {    openSocket = keepAlive;    //Other logic in processSendfile method    return result;}
  • 由上述逻辑可知,openSokcet 又由全局变量 keepAlive 决定。keepAlive 的初始值为 true 。但是会有如下 items 改变其值:
    • Http11Processor 中 service() 方法逻辑:
      int maxKeepAliveRequests = protocol.getMaxKeepAliveRequests();if (maxKeepAliveRequests == 1) {     keepAlive = false;} else if (maxKeepAliveRequests > 0 && socketWrapper.decrementKeepAlive() <= 0) {     keepAlive = false;}
    • 由上面逻辑可知 keepAlive 由 tomcat 每个连接的 maxKeepLiveRequest 决定,其初始默认值为 100。即 server 端每个长连接可以支持 100 个请求,超过就会关闭连接。
    • Http11Processor 中 prepareRequest() 方法逻辑:
      if (protocolMB.equals(Constants.HTTP_10)) {     http11 = false;     keepAlive = false;     protocolMB.setString(Constants.HTTP_10);}MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);if (connectionValueMB != null) {    ByteChunk connectionValueBC = connectionValueMB.getByteChunk();if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {        keepAlive = false;    } else if (findBytes(connectionValueBC, Constants.KEEPALIVE_BYTES) != -1) {        keepAlive = true;    }}
    • 如果是 http1.0 协议则 keepAlive 的值为 false ,关闭 socket 不用长连接,这也符合 http1.0 没有长连接的标准。
    • 对于 http1.1 协议会根据 Connection 请求头的值决定,如果为 close 则表示关闭 socket 不用长连接,如果为 keep-alive 则表示使用长连接,这也符合 http1.1 对长连接定义的标准。

除了以上在 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 ,从而不保持长连接。

根据以上分析对于 tomcat 长连接的总结如下:

  • tomcat 默认就是开启长连接的。
  • 对于 http1.0 协议不使用长连接。
  • 如果请求头中 Connection 的值为 keep-alive 则使用长连接,为 close 则关闭 socket 不使用长连接。
  • tomcat 每个长连接默认支持 100 个请求,如果超过则关闭 socket 停止当前长连接,不过在后续新的连接里还是继续支持长连接。
  • 对于每个长连接 tomcat 会在以前文章介绍的 poller 线程中检查是否有读写超时,默认读写超时时间均为 1 分钟,如果 1 分钟之内没有读写操作,那么关闭 socket 停止当前长连接。
  • 对于上面没有读写操作关闭长连接的情况不仅仅适用于 http 协议,还适用于其它基于 tcp 的协议,因为这个是基于原始 socket 的检测,例如 websocket 协议。 只是对于 websocket 协议来说服务器设置的默认读写超时时间为-1,即不会超时,所以实现了该协议的长连接。当然对于 websocket 协议来说本身也有 ping/pong 定义来实现 keeplive 。
目前先写到这里,下一篇文章里我们继续介绍 tomcat 中的文件上传 。

c94f36a432fcb89e351c045c2cf8b354.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值