系列文章目录
前言
此文以Java中sun.net.www.protocol.http.HttpURLConnection为例,描述Http Keep-Alive的具体实现方式。
环境:
- Java11
- springboot2 + undertow
HttpURLConnection类图+Http链接建立流程
在Websocket系列 --前世今生 中已经描述了Http的Keep-Alive,实际上就是利用了TCP的keep-alive机制,在http建立链接时,尽量利用已经建立好的TCP链接,这里需要强调一下,TCP链接的保活机制是由操作系统(网络分层结构的TCP层)完成的,应用(例如Java应用)只需要设置对应的Keep-Alive参数(例如:keepAliveTimeout和keepAliveConnections)即可。
下图以sun.net.www.protocol.http.HttpURLConnection为例,列出类图和对应的Http链接建立流程。* 第一步,创建HttpURLConnection连接,上代码:
// url为String类型的url地址,此处只支持HTTP,如果要支持HTTPS,参见sun.net.www.protocol.https.HttpsURLConnectionImpl,此类继承自javax.net.ssl.HttpsURLConnection
URL url = new URL(uri);
// 此处如果需要支持HTTPS,只需把HttpURLConnection类型,换为HttpsURLConnection即可
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
- 第二步,创建或重用已有HttpClient
上代码(sun.net.www.http.HttpClient):
1, 创建HttpClient的逻辑
public static HttpClient New(URL url, Proxy p, int to, boolean useCache, HttpURLConnection httpuc) throws IOException
{
........
HttpClient ret = null;
/* see if one's already around */
if (useCache) {
// 从KeepAliveCache读取,Key是URL类
ret = kac.get(url, null);
......
}
......
}
2, HttpClent使用结束后,将其放入KeepAliveCache中
/* return it to the cache as still usable, if:
* 1) It's keeping alive, AND
* 2) It still has some connections left, AND
* 3) It hasn't had a error (PrintStream.checkError())
* 4) It hasn't timed out
*
* If this client is not keepingAlive, it should have been
* removed from the cache in the parseHeaders() method.
*/
public void finished() {
if (reuse) /* will be reused */
return;
keepAliveConnections--;
poster = null;
if (keepAliveConnections > 0 && isKeepingAlive() &&
!(serverOutput.checkError())) {
/* This connection is keepingAlive && still valid.
* Return it to the cache.
*/
putInKeepAliveCache();
} else {
closeServer();
}
}
- Response Header设置
默认HttpURLConnection的Keep-Alive时间为5秒,参见下述代码
// 此代码来自sun.net.www.http.HttpClient.parseHTTPHeader方法
HeaderParser p = new HeaderParser(responses.findValue("Keep-Alive"));
/* default should be larger in case of proxy */
keepAliveConnections = p.findInt("max", usingProxy?50:5);
keepAliveTimeout = p.findInt("timeout", usingProxy?60:5);
所以,如果想控制HttpClient(或HttpsClient)的链接保活时长,需要依赖于服务响应头的属性(Keep-Alive)设置。以Undertow为例:
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
deploymentInfo.addInitialHandlerChainWrapper(handler -> {
// 在响应头中,添加:Keep-Alive: timeout=60
// 时间单位为秒,同时也可以再添加max属性,设置最大保活个数
// 具体协议定义,参见https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive
return new SetHeaderHandler(handler, "Keep-Alive", "timeout=60");
});
});
总结
sun.net.www.protocol.http.HttpURLConnection(或sun.net.www.protocol.https.HttpsURLConnectionImpl)依赖于服务端响应头中属性的设置,考虑到nginx等代理可能重置一些响应头的属性,所以HttpURLConnection保活机制的可配置性并不能得到保证。后续会介绍一下okhttp3的Keep-Alive的实现方式。
版权声明:本文为Justinjiang1314原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。