一、什么是KeepAlive?

KeepAlive 分为TCP的 KeepAlive 和 HTTP的 Keep-Alive,两者是完全不同的概念,不能混为一谈。

1.1、TCP的keepalive

  1. 侧重在保持客户端和服务端的连接,一方会不定期发送心跳包给另一方,当一方挂掉的时候,没有挂掉的一方会定时发送几次心跳包,如果间隔发送几次,对方都返回的是RST,而不是ACK,那么就释放当前链接。
  2. 概括的讲:TCP的keepalive 就是查看客户端和服务端是否都在线,当有一方不在线的时候就释放连接。防止连接一直没有释放,造成服务器资源浪费。

1.2、HTTP的keep-alive

过程:

普通的http连接是客户端连接上服务端,然后结束请求后,由客户端或者服务端进行http连接的关闭。
下次再发送请求的时候,客户端再发起一个连接,传送数据,关闭连接。
这么个流程反复。但是一旦客户端发送connection:keep-alive头给服务端,且服务端也接受这个keep-alive的话,两边对上暗号,这个连接就可以复用了,一个http处理完之后,另外一个http数据直接从这个连接走了。

作用:

减少新建和断开TCP连接的消耗。

1.3、二者的作用

HTTP的Keep-Alive意图在于短时间内连接复用,希望可以短时间内在同一个连接上进行多次请求/响应。

TCP的KeepAlive机制意图在于保活、心跳,检测连接错误。当一个TCP连接两端长时间没有数据传输时(通常默认配置是2小时),发送keepalive探针,探测链接是否存活。

1.1、

二、TCP的KeepAlive

下面是我通过wireshark抓取的一个TCP建立握手的过程。

docker 和防火墙 防火墙keepalive_HTTP

TCP抓包

现在我看只要看前3行,这就是TCP三次握手的完整建立过程,第一个报文SYN从发起方发出,第二个报文SYN,ACK是从被连接方发出,第三个报文ACK确认对方的SYN,ACK已经收到,如下图:

docker 和防火墙 防火墙keepalive_TCP_02

但是数据实际上并没有传输数据,第四个报文才是数据传输开始的过程,细心的读者应该能够发现wireshark把第四个报文解析成HTTP协议,HTTP协议的GET方法和URI也解析出来,所以说TCP层只是通信,只有http才是请求。

当链接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当链接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有数据传输,链接还需不需要保持,这种情况在TCP协议设计中是需要考虑到的。

TCP协议通过一种巧妙的方式去解决这个问题,当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。

怎么开启KeepAlive?

KeepAlive并不是默认开启的,在Linux系统上没有一个全局的选项去开启TCP的KeepAlive。需要开启KeepAlive的应用必须在TCP的socket中单独开启。

Linux Kernel有三个选项影响到KeepAlive的行为:

tcp_keepalive_time 7200// 距离上次传送数据7200秒后,未收到新报文后,就发起检测,默认7200
tcp_keepalive_intvl 75// 检测开始每75秒发送心跳包,默认75
tcp_keepalive_probes 9// 发送9次心跳包对方未响应则close连接,默认9

TCP socket也有三个选项和内核对应,针对单独的socket进行设置:

TCPKEEPCNT: 覆盖 tcpkeepaliveprobes
TCPKEEPIDLE: 覆盖 tcpkeepalivetime
TCPKEEPINTVL: 覆盖 tcpkeepalive_intvl
tcp自带的keepalive的不足和局限性
  1. TCP keepalive 只能检测连接是否存活,不能检测连接是否可用。
    例如,某一方发生了死锁,无法在连接上进行任何读写操作,但是操作系统仍然可以响应网络层keepalive包。
  2. TCP keepalive 机制依赖于操作系统的实现,灵活性不够,默认关闭
  3. 默认的 keepalive 心跳时间是 两个小时, 时间较长。
  4. 代理(如socks proxy)、或者负载均衡器,会让tcp keep-alive失效

三、HTTP的Keep-Alive

背景:

一个网页中,可能引入了几十个js、css文件,上百张图片,如果每请求一个资源,就创建一个连接,然后关闭,代价实在太大了。

作用:

短时间内,在加载同一个网页中的内容时,http链接尽量的复用起来,解决上面的问题,这就是HTTP协议中keep-alive属性的作用。

HTTP的Keep-Alive是HTTP1.1中默认开启的功能。通过headers设置"Connection: close "关闭

对于客户端来说,不论是浏览器,还是手机App,或者我们直接在Java代码中使用HttpUrlConnection,只是负责在请求头中设置Keep-Alive。
Keep-Alive属性保持连接的时间长短是由服务端决定的,通常配置都是在几十秒左右。

TCP连接建立之后,HTTP协议使用TCP传输HTTP协议的请求(Request)和响应(Response)数据,一次完整的HTTP事务如下图

docker 和防火墙 防火墙keepalive_服务端_03

从图中可以发现一个完整的HTTP事务,有链接的建立,请求的发送,响应接收,断开链接这四个过程,我们要展现一张完整的页面需要很多个请求才能完成,如图片.JS.CSS等,如果每一个HTTP请求都需要新建并断开一个TCP,这个开销是完全没有必要的。

开启HTTP Keep-Alive之后,能复用已有的TCP链接,当前一个请求已经响应完毕,服务器端没有立即关闭TCP链接,而是等待一段时间接收浏览器端可能发送过来的第二个请求,通常浏览器在第一个请求返回之后会立即发送第二个请求,如果某一时刻只能有一个链接,同一个TCP链接处理的请求越多,开启KeepAlive能节省的TCP建立和关闭的消耗就越多。

当然通常会启用多个链接去从服务器器上请求资源,但是开启了Keep-Alive之后,仍然能加快资源的加载速度。