Websocket系列 -- Http Keep-Alive实现之sun.net.www.protocol.http.HttpURLConnection

系列文章目录

Websocket系列 --前世今生

Websocket系列 – 协议详解


前言

此文以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版权协议,转载请附上原文出处链接和本声明。