在上一篇文章里我们主要介绍了 tomcat nio 中 block poller 线程的阻塞与唤醒,当 tomcat io 线程读取请求实体数据不可读或者写入响应数据不可写的时候,就会注册事件到 block poller 线程中,并阻塞当前线程。block poller 线程负责注册并监测可读可写到原始 socket ,当可读可写的时候唤醒阻塞的线程,让其继续处理读写事件。在这里我们主要介绍 tomcat 中的长连接。
tomcat 数据的读写都在 io 线程中,根据以前文章 io 线程序列图如下:
对于 SocketProcessor 类,决定是否保持长连接的核心代码如下:
// 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 表明握手有问题,调用 poller.cancelledKey() 方法来关闭原始 socket 不使用长连接。
握手正常,取决 ConnectionHandler.process() 返回的 SocketState 。如果状态为 CLOSED 则直接关闭原始 socket ,否则将不关闭保持长连接。
根据以前文章,ConnectionHandler 实例的 process() 方法会间接的调用Http11Processor 的 service() 方法来决定返回的 SocketState ,其核心逻辑如下:
由上可知 SocketState 由 openSocket 全局变量决定,为 false 的时候关闭连接,为 true 的时候不关闭连接。
openSocket 全局变量又会在 Http11Processor 的 service() 方法中通过调用 processSendfile() 方法改变,逻辑如下:
由上述逻辑可知,openSokcet 又由全局变量 keepAlive 决定。keepAlive 的初始值为 true 。但是会有如下 items 改变其值:
除了以上在 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);
}
}
}
根据以上分析对于 tomcat 长连接的总结如下:
目前先写到这里,下一篇文章里我们继续介绍 tomcat 中的文件上传。