关于HTTP-Alive应该知道的事

总体概述

七层协议是一个广为人知的协议,tcp协议是在传输层,http协议是在应用层,也就是说客户端与服务器端先建立tcp连接,然后在tcp连接的基础上传送http报文。

http协议是一个请求-应答的模式,也就是当没有启动keep-alive的时候,每一次建立http连接都是现用现建立,用完就断开的工作样式。而如果开启了keep-alive模式的话,客户端和服务器之间http连接就会被保持,不会断开(超过Keep-Alive规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。

Keep-Alive的规定时间在客户端(浏览器里)是如何确定的呢?例如Keep-Alive: timeout=5, max=100,表示这个TCP通道可以保持5秒,max=100表示这个长连接最多接收100次请求就断开。

Keep-alivehttp 1.1版本里是默认开启的,只有加入Connection: close才会关闭,现在大部分浏览器都是使用http 1.1协议,所以说在客户端已经是默认发起keep-alive的连接请求。但是能否会完成一个完整的keep-alive还要看服务器端的具体配置情况。

nginx里就直接支持keepalive_timeout指令,其使用0值来停用keep-alive,举例配置如下:

1
2
3
4
5
location /XXX/ {   
alias /url/var/www/html/;
keepalive_timeout 75;
expires 5m;
}

使用长连接之后,客户端和服务端怎么知道本次传输结束呢?两部分:1. 判断传输数据是否达到了Content-Length指示的大小,这个是最简单的最傻瓜的,普遍应用于静态的图片或者页面;2. 往往动态生成的文件没有Content-Length,它是分块传输(chunked),这时候怎么办呢?就要根据chunked编码来判断,chunked编码的数据在最后有一个空chunked块,表明本次传输数据结束,这种情况更多应用于动态的页面。

进一步的说chunked

HTTP请求报文的格式是这样的:

1
2
3
4
<method> <request-URL> <version>
<headers>

<entity-body>

其中在请求头的地方有一个叫Content-Length的字段,如果没有这个字段那么就会有叫Transfer-encoding的字段,它用来表示http报文的传输格式,这个字段的取值有很多,但是真正有意义的只有一个—chunked

如果一个HTTP消息(请求消息或应答消息)的Transfer-Encoding消息头的值为chunked,那么,消息体由数量未定的块组成,并以最后一个大小为0的块为结束。

每一个非空的块都以该块包含数据的字节数(字节数以十六进制表示)开始,跟随一个CRLF(回车及换行),然后是数据本身,最后块CRLF结束。在一些实现中,块大小和CRLF之间填充有白空格(0x20)。

最后一块是单行,由块大小(0)、一些可选的填充白空格、以及CRLF组成。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。消息最后以CRLF结尾。

注意1.chunkedmultipart两个名词在意义上有类似的地方,不过在HTTP协议当中这两个概念则不是一个类别的。multipart是一种Content-Type,标示HTTP报文内容的类型,而chunked是一种传输格式,标示报头将以何种方式进行传输;

注意2.chunked传输不能事先知道内容的长度,只能靠最后的空chunk块来判断,因此对于下载请求来说,是没有办法实现进度的。在浏览器和下载工具中,偶尔我们也会看到有些文件是看不到下载进度的,即采用chunked方式进行下载;

注意3.chunked的优势在于,服务器端可以边生成内容边发送,无需事先生成全部的内容。HTTP/2不支持Transfer-Encoding: chunked,因为HTTP/2有自己的streaming传输方式。

http keep-alive与tcp keep-alive

http的keep-alive与tcp的keep-alive可不是同一回事,意图也不一样。http的keep-alive是为了让tcp活得更久一点,以便在同一个连接上传送多个http,提高socket的效率。而tcp的keep-alive是tcp的一种检测tcp连接状况的保鲜机制。tcp的keep-alive是一个保鲜定时器,支持三个系统内核配置参数:

1
2
3
echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes

keepalive是TCP保鲜定时器,当网络两端建立了tcp连接之后,闲置idle(双方没有任何数据流发送往来)了tcp_keepalive_time后,服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对对方的ack,如果一直没有收到对方的ack,一共会尝试tcp_keepalive_probes次,每次的间隔时间在这里分别是15s、30s、45s、60s、75s。如果尝试tcp_keepalive_probes,依然没有收到对方的ack包,则会丢弃该TCP连接。TCP连接默认闲置时间是2小时,一般设置为30分钟足够了。

也就是说,仅当nginxkeepalive_timeout值设置高于tcp_keepalive_time,并且距此tcp连接传输的最后一个http响应,经过了tcp_keepalive_time时间之后,操作系统才会发送侦测包来决定是否要丢弃这个TCP连接。一般不会出现这种情况,除非你需要这样做。

keep-alive与TIME_WAIT

使用http的keep-alive,可以减少服务端TIME_WAIT数量(因为由服务端httpd守护进程主动关闭连接)。道理很简单,相较而言,启用keep-alive,建立的tcp连接更少了,自然要被关闭的tcp连接也相应更少了。

补充

建议在服务器提供Web站点服务时(一个页面除了动态内容,还包含非常多的JS、图片、css文件等)开启keep-alive。在“服务器提供的是一个接口服务,除了动态内容,几乎没有引用任何静态内容”这样的场景,不建议开启keep-alive

参考资料

http://www.cnblogs.com/skynet/archive/2010/12/11/1903347.html
https://hit-alibaba.github.io/interview/basic/network/HTTP.html
http://51write.github.io/2014/04/09/keepalive/
http://www.nowamagic.net/academy/detail/23350305

感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------