Tomcat高效响应的秘密 keep alive
Posted Tomcat那些事儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat高效响应的秘密 keep alive相关的知识,希望对你有一定的参考价值。
众所周知,HTTP是TCP/IP的上层协议。而基于TCP/IP的协议,都会涉及到三次握手建立连接,四次挥手结束连接。当然,在请求数量不多的情况下,这些还勉强可以接受。但在互联网上,各种HTTP的请求,动辄成百上千的连接请求,如果每个都握手、挥手,基本都干这些了,正常的数据传输反而不能及时送达了。
为此,在HTTP1.1的时候,将keep alive设置为默认的,以提高HTTP的效率。下图是维基百科上关于keep alive的一个配图,对比打开和关闭keep alive情况下的传输效果。
keep alive,又称为persistent connection,或Http connection reuse。主要作用是用一个TCP连接来处理多个HTTP的请求和响应。好处自然是相当明显的,节省了创建和结束多个连接的时间,可以更专注于数据的传输,页面可以更快的渲染,同时也降低了服务器的负载。
以下几张图来自图解HTTP一书,也很好的说明keep alive的原理
单个请求的情况:
多个请求的情况:
使用keep alive后的情况:
基本原理就是上面这些,而我们在发送一个HTTP请求时,HTTP1.1的请求头中会自动包含connection: keep alive这样的内容。
那是不是keep alive一直开启着就一直有优势呢?
事情都是有两面性的,如果一直开启,那在大量请求到来时,势必会创建大量的连接,而这些连接一直keep alive,而请求量下降的时候,服务器上会存在不少不工作的连接,依然会影响性能。
下面我们来看,在Tomcat中,对于keep alive是怎么实现的。
前面的文章里写过关于Tomcat的几类Connector,可以看这里
几类Connector包含一个抽象基类AbstractHttp11Processor
在该类中会进行keep alive逻辑的判断处理。
上面内容说keep alive默认是开启的,当然也是可以显式的关闭的,只要在请求头中设置connection:close就可以了。
代码中先是判断请求头中是否包含关闭的配置
MimeHeaders headers = request.getMimeHeaders();
// Check connection header
MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);
if (connectionValueMB != null) {
ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {// 判断是否有显式的关闭keep-alive
keepAlive = false;
} else if (findBytes(connectionValueBC,
Constants.KEEPALIVE_BYTES) != -1) {
keepAlive = true;
}
}
同时,在Connector中还可以配置,允许keep alive的最大连接数,
if (maxKeepAliveRequests == 1) {
keepAlive = false;
} else if (maxKeepAliveRequests > 0 &&
socketWrapper.decrementKeepAlive() <= 0) { // 判断允许keep alive的请求数还有多少
keepAlive = false;
}
同时在特定条件下,已经开启的keep alive 连接也是要关闭的
// Connection: close header.
keepAlive = keepAlive && !statusDropsConnection(statusCode); // 根据status code判断是否要把当前连接丢掉
if (!keepAlive) {
// Avoid adding the close header twice
if (!connectionClosePresent) {
headers.addValue(Constants.CONNECTION).setString( // 设置响应头中的connection为close
Constants.CLOSE);
}
} else if (!http11 && !getErrorState().isError()) {
headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
}
对应的以下几类响应状态,keep alive会关闭
/**
* Determine if we must drop the connection because of the HTTP status
* code. Use the same list of codes as Apache/httpd.
*/
protected boolean statusDropsConnection(int status) {
return status == 400 /* SC_BAD_REQUEST */ ||
status == 408 /* SC_REQUEST_TIMEOUT */ ||
status == 411 /* SC_LENGTH_REQUIRED */ ||
status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
status == 414 /* SC_REQUEST_URI_TOO_LONG */ ||
status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
status == 501 /* SC_NOT_IMPLEMENTED */;
}
在服务端传回响应头中包含close,则该连接会关闭。
而实质上,每次的keep alive,是在一个循环里跑,直到到达配置的超时时间后,退出循环。退出时的根据最终Socket的状态,判断是否要保持该keep alive的连接。如果要保持,则会重新添加到线程池中继续执行,例如NIO的处理时,在循环跳出后,finally中最终会执行以下代码,将SocketProcessor添加到cache中。
if (running && !paused) {
processorCache.push(this); // 重点是这里。
}
而Poller在处理特定的事件时,会直接从cache中pop出来
SocketProcessor sc = processorCache.pop(); // 看这里
if ( sc == null ) sc = new SocketProcessor(attachment, status);
else sc.reset(attachment, status);
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
BIO中,则是直接将SocketProcessor传到线程池的Executor中。
getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));
相关阅读
以上是关于Tomcat高效响应的秘密 keep alive的主要内容,如果未能解决你的问题,请参考以下文章