一、长连接
在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