JDK对Http协议的Keep-Alive的支持,以JDK8为例

Http协议对keep-alive的支持

​ keep-alive顾名思义就是保持连接的意思,在早期的HTTP/1.0中,每次http请求都要创建一个连接,而创建连接的过程需要消耗资源和时间,为了减少资源消耗,缩短响应时间,就需要重用连接。
​ 在后来的HTTP/1.0中以及HTTP/1.1中,引入了重用连接的机制,就是在http请求头中加入Connection: keep-alive来告诉对方这个请求响应完成后不要关闭,下一次咱们还用这个请求继续交流。
​ 协议规定HTTP/1.0如果想要保持长连接,需要在请求头中加上Connection: keep-alive,而HTTP/1.1默认是支持长连接的,有没有这个请求头都行。另外,一般服务端都会设置keep-alive超时时间。超过指定的时间间隔,服务端就会主动关闭连接。同时服务端还会设置一个参数叫最大请求数,比如当最大请求数是300时,只要请求次数超过300次,即使还没到超时时间,服务端也会主动关闭连接。如下图所示为服务器返回的Responser Headers的值。

Reponse HeadersConnection: Keep-AliveContent-length: 35content-type: application/json;charset=UTF-8Keep-Alive: timeout=60,max=5

​ 注意,当Connection:keep-alive存在时,下面的Keep-Alive: timeout=70, max=10才会生效。

​ Http协议对keep-alive的支持是基于TCP连接的成功建立,而TCP协议是对Http透明的,即TCP协议的Keep-Alive与Http的Keep-Alive是无关的。

TCP协议中的keep-Alive

​ TCP keepalive指的是TCP保活计时器(keepalive timer)。设想有这样的情况:客户已主动与服务器建立了TCP连接。但后来客户端的主机突然出故障。显然,服务器以后就不能再收到客户发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就是使用保活计时器。服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75秒发送一次。若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出了故障,接着就关闭这个连接。

——摘自谢希仁《计算机网络》

JDK对Http协议的keep-alive的支持

​ JDK对Http协议的Keep-Alive的支持参考oracel官方说明https://docs.oracle.com/javase/8/docs/technotes/guides/net/http-keepalive.html。接下来结合jdk源码分析:

1、HttpURLConnection(java.net.HttpURLConnection)使用长连接

​ JDK自带的HttpURLConnection,默认启用keepAlive,支持HTTP / 1.1和HTTP / 1.0持久连接, 使用后的HttpURLConnection会放入缓存中供以后的同host:port的请求重用,底层的socket在keepAlive超时之前不会关闭。

​ HttpURLConnection受以下system properties控制:
​ http.keepAlive=(默认值:true),是否启用keepAlive,如果设置为false,则HttpURLConnection不会缓存,使用完后会关闭socket连接。(可设置
​ http.maxConnections=(默认值:5),每个目标host缓存socket连接的最大数。(当http.keepAlive=false时为1,否则为5,下面结合源码分析)

2、sun.net.www.http包下的HttpURLConnection和HttpClient长连接缓存

​ sun.net.www.http.HttpURLConnection是java.net.HttpURLConnection的实现类,JDK自带的HttpURLConnection底层使用JDK自带的HttpClient发送http请求,java8的HttpClient(sun.net.www.http.HttpClient)存在诸多限制,在Jdk11中,java.net.HttpClient吸收第三方HttpClient的优点,性能肩比第三方HttpClient(okHttptClitn,apache Httpclient)。

​ 下面为sun.net.www.http.HttpURLConnection.HttpURLConnection类,有如下两个方法返回HttpClient的实例和直接关闭socket的disconnect()方法。

public class HttpURLConnection extends java.net.HttpURLConnection {// subclass HttpsClient will overwrite & return an instance of HttpsClientprotected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)throws IOException {return HttpClient.New(url, p, connectTimeout, this);}// subclass HttpsClient will overwrite & return an instance of HttpsClientprotected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout, boolean useCache)throws IOException {return HttpClient.New(url, p, connectTimeout, useCache, this);}/*** Disconnect from the server (public API)*/public void disconnect() {}
}

​ 下面为HttpClient类,parseHTTPHeader会解析header参数,判断HttpURLConnection是否是长连接,如果是长连接会读取keepAliveTimeout参数作为KeepAliveCache类里的长连接的缓存有效时间(默认为0)。并设置keepAliveConnections的值,如过为长连接值为5,否则为1。

​ 完成获取请求返回结果后或调用getInputStream().close(),JDK会清理连接并作为以后使用的连接缓存。具体逻辑为执行finished()方法,如果是短连接,直接关闭套接字(调用closeServer()方法),如果是长连接,则将这个长连接加到KeepAliveCache的缓存线程中(putInKeepAliveCache()方法)。

​ 其中,protected static KeepAliveCache kac = new KeepAliveCache();代码表面运行时只有全局只有一个缓存的线程类。

public class HttpClient extends NetworkClient {// whether this httpclient comes from the cacheprotected boolean cachedHttpClient = false;protected boolean inCache;/* where we cache currently open, persistent connections */protected static KeepAliveCache kac = new KeepAliveCache();private static boolean keepAliveProp = true;private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc){keepAliveConnections = -1;keepAliveTimeout = 0;}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();}}protected synchronized void putInKeepAliveCache() {if (inCache) {assert false : "Duplicate put to keep alive cache";return;}inCache = true;kac.put(url, null, this);}
}

​ KeepAliveCache继承了HashMap,实现了Runnable接口。

​ 它本身可以存储不同的KeepAliveKey-ClientVector,KeepAliveKey是关于协议(一般为http),ip,port类,对应了一个socket连接。ClientVector实现了双端队列,存储了HttpClient实例和idleStartTime(该HttpClient开始空闲的时间),最多可以存5个,对应HttpClient类的keepAliveConnections。ClientVector还有一个属性nap,即缓存过期时间,在put()方法实例化时设置,new ClientVector(keepAliveTimeout > 0 ? keepAliveTimeout * 1000 : LIFETIME)

​ KeepAliveCache也是一个线程类,run方法的逻辑是do-while循环检测HashMap中缓存的长连接是否timeout,如果超时就清理,当HashMap为空时,线程完成自己的逻辑,执行完毕。

​ KeepAliveCache提供put()方法,运行时KeepAliveCache线程类全局只有一个,没有缓存的线程类时,在put()方法中创建该缓存类keepAliveTimer,并设置长连接的缓存时间。

public class KeepAliveCacheextends HashMap<KeepAliveKey, ClientVector>implements Runnable {/* Sleeps for an alloted timeout, then checks for timed out connections.* Errs on the side of caution (leave connections idle for a relatively* short time).*/@Overridepublic void run() {do {try {Thread.sleep(LIFETIME);} catch (InterruptedException e) {}// Remove all outdated HttpClients.synchronized (this) {long currentTime = System.currentTimeMillis();List<KeepAliveKey> keysToRemove = new ArrayList<>();for (KeepAliveKey key : keySet()) {ClientVector v = get(key);synchronized (v) {KeepAliveEntry e = v.peek();while (e != null) {if ((currentTime - e.idleStartTime) > v.nap) {v.poll();e.hc.closeServer();} else {break;}e = v.peek();}if (v.isEmpty()) {keysToRemove.add(key);}}}for (KeepAliveKey key : keysToRemove) {removeVector(key);}}} while (!isEmpty());}/*** Register this URL and HttpClient (that supports keep-alive) with the cache* @param url  The URL contains info about the host and port* @param http The HttpClient to be cached*/public synchronized void put(final URL url, Object obj, HttpClient http) {boolean startThread = (keepAliveTimer == null);if (!startThread) {if (!keepAliveTimer.isAlive()) {startThread = true;}}if (startThread) {clear();/* Unfortunately, we can't always believe the keep-alive timeout we got* back from the server.  If I'm connected through a Netscape proxy* to a server that sent me a keep-alive* time of 15 sec, the proxy unilaterally terminates my connection* The robustness to get around this is in HttpClient.parseHTTP()*/final KeepAliveCache cache = this;AccessController.doPrivileged(new PrivilegedAction<>() {public Void run() {keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);keepAliveTimer.setDaemon(true);keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);keepAliveTimer.start();return null;}});}KeepAliveKey key = new KeepAliveKey(url, obj);ClientVector v = super.get(key);if (v == null) {int keepAliveTimeout = http.getKeepAliveTimeout();v = new ClientVector(keepAliveTimeout > 0 ?keepAliveTimeout * 1000 : LIFETIME);v.put(http);super.put(key, v);} else {v.put(http);}}class ClientVector extends ArrayDeque<KeepAliveEntry> {// sleep time in milliseconds, before cache clearint nap;ClientVector(int nap) {this.nap = nap;}}class KeepAliveKey {private String      protocol = null;private String      host = null;private int         port = 0;private Object      obj = null; // additional key, such as socketfactory/*** Constructor* @param url the URL containing the protocol, host and port information*/public KeepAliveKey(URL url, Object obj) {this.protocol = url.getProtocol();this.host = url.getHost();this.port = url.getPort();this.obj = obj;}}class KeepAliveEntry {HttpClient hc;long idleStartTime;KeepAliveEntry(HttpClient hc, long idleStartTime) {this.hc = hc;this.idleStartTime = idleStartTime;}}
}
3、HttpURLConnection使用短连接
3.1)使用短连接,不缓存长连接

​ 设置System.setProperty(“http.keepAlive”, ”false”);将整个 APP 的 http 长连接支持关掉。不会启动新的线程去缓存HttpClient连接,在HttpClient类的finished方法里不执行缓存连接的操作,直接关闭socket连接。

3.2)客户端不使用keep-alive功能

​ HttpURLConnection的实例获取到服务方的数据后直接关闭HttClient(关闭socket),不缓存如下所示:

//第一种,Header指定短连接
httpConn.setRequestProperty("Connection", "close");
//第二种,请求完后直接关闭socket
httpURLConnection.disconnect();
3.3)服务端长连接关闭

​ 返回的Response Header中包含Connection:close即可。

参考另外几位大佬们的文章:
http://www.itersblog.com/archives/3.html
https://blog.csdn.net/u012216753/article/details/78084327
https://blog.csdn.net/tianshouzhi/article/details/103922842?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-5&spm=1001.2101.3001.4242

JDK对Http协议的Keep-Alive的支持,以JDK8为例相关推荐

  1. Unity(支持WebGL)+PHP(Workerman的Gateway)用Websocket协议实现匹配对战(摇骰子为例)2

    Unity(支持WebGL)+PHP(Workerman的Gateway)用Websocket协议实现匹配对战(摇骰子为例)1 上篇文章只讲述工程使用.发布.并没讲述任何流程和实现原理.然而下面先讲述 ...

  2. Unity(支持WebGL)+PHP(Workerman的Gateway)用Websocket协议实现匹配对战(摇骰子为例)1

    文章目录 服务端PHP(Windows下演示) 安装PHP 启动服务器 结束服务器 客户端Unity(版本Unity2021.3.5f1) 1.发布Windows客户端 ws/wss(不发布微信小游戏 ...

  3. SW3518全协议OPPO闪充、支持QC-A+C-PD 口任意口快充输出车充IC

    深圳市展嵘电子有限公司 有需要的上帝可联系小陈:136-6225-3950 : 3412-1522-98 支持 PD 的多快充协议双口充电解决方案 概述 SW3518 是一款高集成度的多快充协议双口充 ...

  4. ssh协议是tcp还是udp_DNS 支持 TCP 和 UDP 双协议,但为何偏偏只钟情 UDP?

    一.前言 之前在聊到 App 网络优化时,聊到通过 HTTPDNS 替换掉传统的 DNS 解析,来达到网络优化的效果.其中提到 DNS 解析,是支持 UDP 和 TCP 双协议的. 但是细心的朋友通过 ...

  5. PD协议芯片TYPE C接口支持快充诱骗

    是一颗符合PD.QC2.0.QC3.0.AFC.FCP协议的电源受电端(诱骗)协议芯片,从充电器(如PD或QC协议充电器)上诱骗出合适的电压给产品供电.可广泛应用于各个领域的各种产品,如无线充电.小家 ...

  6. Android Bluetooth蓝牙开发\蓝牙协议\蓝牙通信例子_Android支持蓝牙4.0版本_BLE开发

    一.Android Bluetooth现状 在android官网可以了解到android4.2新增了部分新功能,但是对于BT熟悉的人或许开始头疼了,那就是Android4.2引入了一个新的蓝牙协议栈针 ...

  7. 诱骗芯片PD+QC协议XSP06应用案例,支持5V9V12V15V20V

    一.概述 XSP06是一款符合USB Power Delivery 标准的PD.QC.AFC协议的受电端(诱骗)协议芯片,支持从手机充电器/车充等电源上诱骗出需要的电压给产品供电.支持固定电压模式和使 ...

  8. SVN+SSH协议工作方式全解析,以Sourceforge为例讲解如何在Windows下配置TortoiseSVN和Su

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 简单地说 ...

  9. 服务器协议密码,确定SSL服务器支持的最差/最佳协议、最弱/最强密码的最快速方法?...

    为此,我需要找出服务器支持的最差/最佳协议和最弱/最强的密码.在 以下是我使用sslyze的代码:from plugins import PluginOpenSSLCipherSuites from ...

最新文章

  1. python制作图形化界面_python图形化界面设计(wxpython)一布局管理
  2. 如何在使用eclipse的情况下,清理android项目中的冗余class文件和资源文件以及冗余图片...
  3. Composer 中国全量镜像(二)
  4. ActiveReports 报表应用教程 (10)---交互式报表之向下钻取(详细数据按需显示解决方案)...
  5. 基于LINQ to SQL的WEB开发三层架构(2)
  6. select count(*)和select count(1)
  7. ROS中阶笔记(一):机器人系统设计—ROS系统下连接外部传感器
  8. this.$modal.confirm 自定义按钮关闭_【勤哲资料】7.6 自定义打印
  9. windows 给安装目录命令权限
  10. svn+ssh 想要CheckOut不容易
  11. 指令重排序导致的可见性问题
  12. Jmeter学习笔记4-集合点
  13. 吴声年度演讲全文:场景品牌,新商业的此时此刻
  14. 第一篇:什么是IT行业
  15. Lucid Dream
  16. 平衡网站的色彩各种颜色的意义
  17. Excel插件快捷键弹窗事件(VSTO+键盘钩子实现)
  18. Java公式编辑器开发思路,附项目源码
  19. 一些人的RHCE的考试经历(值得我们去借鉴)
  20. 系统的软中断CPU使用率升高,我该怎么办?

热门文章

  1. Lagrange Multiplier Theorem——候选人定理
  2. 《如何写好科研论文》
  3. Python大小写转换
  4. python的pth打开方式_pytorch 加载(.pth)格式的模型实例
  5. 3D 打印没那么玄乎
  6. 使用python将pdf转化为长图片
  7. 微信在教育行业的营销
  8. 皇甫懒懒 Java 学习笔记 第一章
  9. react在线编辑Excel表格
  10. 题8:二叉树的下一个结点