【http】keep-alive详解

2021/11/09 09:44:39

转载自:CSDN 博主「尐葮阿譽」的原创文章
原文链接:https://blog.csdn.net/xiaoduanayu/article/details/78386508open in new window

HTTP keep-alive 是为了让 TCP 活得更久一点,以便在同一个连接上传送多个 http,提高 socket 的效率。

为什么要有 Connection: 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 默认是支持长连接的,有没有这个请求头都行。

当然了,协议是这样规定的,至于支不支持还得看服务器(比如 tomcat)和客户端(比如浏览器)的具体实现。在实践过程中发现谷歌浏览器使用 HTTP/1.1 协议时请求头中总会带上Connection: keep-alive,另外通过 httpclient 使用 HTTP/1.0 协议去请求 tomcat 时,即使带上Connection: keep-alive请求头也保持不了长连接。如果 HTTP/1.1 版本的 http 请求报文不希望使用长连接,则要在请求头中加上 Connection: close,接收到这个请求头的对端服务就会主动关闭连接。

但是 http 长连接会一直保持吗?肯定是不会的。一般服务端都会设置 keep-alive 超时时间。超过指定的时间间隔,服务端就会主动关闭连接。同时服务端还会设置一个参数叫最大请求数,比如当最大请求数是 300 时,只要请求次数超过 300 次,即使还没到超时时间,服务端也会主动关闭连接。

Transfer-Encoding 和 Content-Length

谈到 http 长连接,都绕不开这两个请求/响应头。其中 Transfer-Encoding 不建议在请求头中使用,因为无法知道服务端能否解析这个请求头,而应该在响应头中使用,因为客户端浏览器都能解析这个响应头。Content-Length 在请求方法为 GET 的时候不能使用,在请求方法为 POST 的时候需要使用,同时也常常出现在响应头中。为了方便描述,下面只说明响应头中出现这两个属性的情况。

要实现长连接很简单,只要客户端和服务端都保持这个 http 长连接即可。但问题的关键在于保持长连接后,浏览器如何知道服务器已经响应完成?在使用短连接的时候,服务器完成响应后即关闭 http 连接,这样浏览器就能知道已接收到全部的响应,同时也关闭连接(TCP 连接是双向的)。在使用长连接的时候,响应完成后服务器是不能关闭连接的,那么它就要在响应头中加上特殊标志告诉浏览器已响应完成。

一般情况下这个特殊标志就是 Content-Length,来指明响应体的数据大小,比如Content-Length: 120表示响应体内容有 120 个字节,这样浏览器接收到 120 个字节的响应体后就知道了已经响应完成。

由于 Content-Length 字段必须真实反映响应体长度,但实际应用中,有些时候响应体长度并没那么好获得,例如响应体来自于网络文件,或者由动态语言生成。这时候要想准确获取长度,只能先开一个足够大的内存空间,等内容全部生成好再计算。但这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。这时候Transfer-Encoding: chunked响应头就派上用场了,该响应头表示响应体内容用的是分块传输,此时服务器可以将数据一块一块地分块响应给浏览器而不必一次性全部响应,待浏览器接收到全部分块后就表示响应结束。

以分块传输一段文本内容:“人的一生总是在追求自由的一生 So easy”来说明分块传输的过程,如下图所示

分块传输过程

图中每个分块的第一行是分块内容的大小,十六进制表示,后面跟 CRLF(\r\n),第一行本身以及分块内容末尾的 CRLF 不计入大小。第二行是分块内容,后面也跟 CRLF。最后一个分块虽然大小为零,但是必不可少,表示分块的结束,后面也跟 CRLF,同时内容为空。最后,响应体以 CRLF 结束。将它们结合起来的响应内容就是:

HTTP/1.1 200 OK
Content-Type: text/plain;charset=utf-8
Connection: keep-alive
Transfer-Encoding: chunked

21\r\n
人的一生总是在追求自由\r\n
11\r\n
的一生 So easy\r\n
0\r\n
\r\n

不过以上格式的响应体内容用浏览器自带的调试工具是看不出来的,浏览器自带调试工具对分块传输和非分块传输响应体的显示是一样的,要想看到区别,需要用 Wireshark、Fiddler 等抓包工具查看。

HTTP keep-alive 和 TCP keepalive 的区别

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

HTTP keep-alive 是为了让 TCP 活得更久一点,以便在同一个连接上传送多个 http,提高 socket 的效率。

TCP keep-alive 是一种检测 TCP 连接状况的保鲜机制。

参考

http 的 keep-alive 和 tcp 的 keepalive 区别open in new window