什么是HTTP Keep-Alive呢?

在经过调试工具查看网络请求的时候,一般在response header能看到相似下面这种:Keep-Alive: timeout=10, max=94 。那么Keep-Alive究竟是什么呢?php

HTTP Keep-Alive

在http早期,每一个http请求都要求打开一个tpc socket链接,而且使用一次以后就断开这个tcp链接。nginx

使用keep-alive能够改善这种状态,即在一次TCP链接中能够持续发送多份数据而不会断开链接。经过使用keep-alive机制,能够减小tcp链接创建次数,也意味着能够减小TIME_WAIT状态链接,以此提升性能和提升httpd服务器的吞吐率(更少的tcp链接意味着更少的系统内核调用,socket的accept()和close()调用)。浏览器

可是,keep-alive并非免费的午饭,长时间的tcp链接容易致使系统资源无效占用。配置不当的keep-alive,有时比重复利用链接带来的损失还更大。因此,正确地设置keep-alive timeout时间很是重要。服务器

keepalvie timeout

Httpd守护进程,通常都提供了keep-alive timeout时间设置参数。好比nginx的keepalive_timeout,和Apache的KeepAliveTimeout。这个keepalive_timout时间值意味着:一个http产生的tcp链接在传送完最后一个响应后,还须要hold住keepalive_timeout秒后,才开始关闭这个链接。网络

当httpd守护进程发送完一个响应后,理应立刻主动关闭相应的tcp链接,设置 keepalive_timeout后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,即是keepalive_timeout时间。若是守护进程在这个等待的时间里,一直没有收到浏览发过来http请求,则关闭这个http链接。socket

下面写一个脚本,方便测试:tcp

1    sleep(60);  //为了便于分析测试,会根据测试进行调整
2    echo "www.example.com";

1. 当keepalive_timeout时间为0时,即不启用Keep-Alive时,一个tcp链接的生命周期:工具

01    #tcpdump -n host 218.1.57.236 and port 80
02    20:36:50.792731 IP 218.1.57.236.43052 > 222.73.211.215.http: S 1520902589:1520902589(0) win 65535
03    20:36:50.792798 IP 222.73.211.215.http > 218.1.57.236.43052: S 290378256:290378256(0) ack 1520902590 win 5840
04    20:36:50.801629 IP 218.1.57.236.43052 > 222.73.211.215.http: . ack 1 win 32768
05     
06    20:36:50.801838 IP 218.1.57.236.43052 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
07    20:36:50.801843 IP 222.73.211.215.http > 218.1.57.236.43052: . ack 797 win 59
08     
09    20:37:50.803230 IP 222.73.211.215.http > 218.1.57.236.43052: P 1:287(286) ack 797 win 59
10    20:37:50.803289 IP 222.73.211.215.http > 218.1.57.236.43052: F 287:287(0) ack 797 win 59
11    20:37:50.893396 IP 218.1.57.236.43052 > 222.73.211.215.http: . ack 288 win 32625
12    20:37:50.894249 IP 218.1.57.236.43052 > 222.73.211.215.http: F 797:797(0) ack 288 win 32625
13    20:37:50.894252 IP 222.73.211.215.http > 218.1.57.236.43052: . ack 798 win 59
  • 第1~3行创建tcp三次握手,创建链接。用时8898μs
  • 第4~5行经过创建的链接发送第一个http请求,服务端确认收到请求。用时5μs
  • 第5~6行,能够知道脚本执行用时60s1387μs,与php脚本相符。
  • 第六、8行服务端发送http响应。发送响应用时90166μs。
  • 第7行,代表由服务端守护进程主动关闭链接。结合第六、8行,说明http响应一旦发送完毕,服务端立刻关闭这个tcp链接
  • 第七、九、10说明tcp链接顺序关闭,用时90963μs。须要注意,这里socket资源并无当即释放,须要等待2MSL时间(60s)后才被真正释放。

因而可知,在没有设置 keepalive_timeout 状况下,一个socket资源从创建到真正释放须要通过的时间是:创建tcp链接 + 传送http请求 + php脚本执行 + 传送http响应 + 关闭tcp链接 + 2MSL 。(注:这里的时间只能作参考,具体的时间主要由网络带宽,和响应大小而定)性能

2. 当keepalive_timeout时间大于0时,即启用Keep-Alive时,一个tcp链接的生命周期。为了便于分析,咱们将keepalive_timeout设置为300s测试

01    #tcpdump -n host 218.1.57.236 and port 80
02    21:38:05.471129 IP 218.1.57.236.54049 > 222.73.211.215.http: S 1669618600:1669618600(0) win 65535
03    21:38:05.471140 IP 222.73.211.215.http > 218.1.57.236.54049: S 4166993862:4166993862(0) ack 1669618601 win 5840
04    21:38:05.481731 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 1 win 32768
05    21:38:05.481976 IP 218.1.57.236.54049 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
06    21:38:05.481985 IP 222.73.211.215.http > 218.1.57.236.54049: . ack 797 win 59
07     
08    21:38:07.483626 IP 222.73.211.215.http > 218.1.57.236.54049: P 1:326(325) ack 797 win 59
09    21:38:07.747614 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 326 win 32605
10    21:43:07.448454 IP 222.73.211.215.http > 218.1.57.236.54049: F 326:326(0) ack 797 win 59
11    21:43:07.560316 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 327 win 32605
12    21:43:11.759102 IP 218.1.57.236.54049 > 222.73.211.215.http: F 797:797(0) ack 327 win 32605
13    21:43:11.759111 IP 222.73.211.215.http > 218.1.57.236.54049: . ack 798 win 59

 

   
  • 咱们先看一下,第6~8行,跟上次示例不同的是,服务端httpd守护进程发完响应后,没有当即主动关闭tcp链接。
  • 第8行,结合第6行,咱们能够看到,5分钟(300s)后,服务端主动关闭这个tcp链接。这个时间,正是咱们设置的keepalive_timeout的时间。
  • 因而可知,设置了keepalive_timout时间状况下,一个socket创建到释放须要的时间是多了keepalive_timeout时间。

3. 当keepalive_timeout时间大于0,而且在同一个tcp链接发送多个http响应。这里为了便于分析,咱们将keepalive_timeout设置为180s

经过这个测试,咱们想弄清楚,keepalive_timeout是从第一个响应结束开启计时,仍是最后一个响应结束开启计时。测试结果证明是后者,这里,咱们每隔120s发一次请求,经过一个tcp链接发送了3个请求。

01    # tcpdump -n host 218.1.57.236 and port 80
02    22:43:57.102448 IP 218.1.57.236.49955 > 222.73.211.215.http: S 4009392741:4009392741(0) win 65535
03    22:43:57.102527 IP 222.73.211.215.http > 218.1.57.236.49955: S 4036426778:4036426778(0) ack 4009392742 win 5840
04    22:43:57.111337 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1 win 32768
05     
06    22:43:57.111522 IP 218.1.57.236.49955 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
07    22:43:57.111530 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 797 win 59
08    22:43:59.114663 IP 222.73.211.215.http > 218.1.57.236.49955: P 1:326(325) ack 797 win 59
09    22:43:59.350143 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 326 win 32605
10     
11    22:45:59.226102 IP 218.1.57.236.49955 > 222.73.211.215.http: P 1593:2389(796) ack 650 win 32443
12    22:45:59.226109 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 2389 win 83
13    22:46:01.227187 IP 222.73.211.215.http > 218.1.57.236.49955: P 650:974(324) ack 2389 win 83
14    22:46:01.450364 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 974 win 32281
15     
16    22:47:57.377707 IP 218.1.57.236.49955 > 222.73.211.215.http: P 3185:3981(796) ack 1298 win 32119
17    22:47:57.377714 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 3981 win 108
18    22:47:59.379496 IP 222.73.211.215.http > 218.1.57.236.49955: P 1298:1622(324) ack 3981 win 108
19    22:47:59.628964 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1622 win 32768
20     
21    22:50:59.358537 IP 222.73.211.215.http > 218.1.57.236.49955: F 1622:1622(0) ack 3981 win 108
22    22:50:59.367911 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1623 win 32768
23    22:50:59.686527 IP 218.1.57.236.49955 > 222.73.211.215.http: F 3981:3981(0) ack 1623 win 32768
24    22:50:59.686531 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 3982 win 108

 

  • 第一组,三个ip包表示tcp三次握手创建链接,由浏览器创建。
  • 第二组,发送第一次http请求而且获得响应,服务端守护进程输出响应以后,并没立刻主动关闭tcp链接。而是启动keepalive_timout计时。
  • 第三组,2分钟后,发送第二次http请求而且获得响应,一样服务端守护进程也没有立刻主动关闭tcp链接,从新启动keepalive_timout计时。
  • 第四组,又2分钟后,发送了第三次http请求而且获得响应。服务器守护进程依然没有主动关地闭tcp链接(距第一次http响应有4分钟了,大于keepalive_timeout值),而是从新启动了keepalive_timout计时。
  • 第五组,跟最后一个响应keepalive_timeout(180s)内,守护进程再没有收到请求。计时结束,服务端守护进程主动关闭链接。4次挥手后,服务端进入TIME_WAIT状态。

这说明,当设定了keepalive_timeout,一个socket由创建到释放,须要时间是:tcp创建 + (最后一个响应时间 – 第一个请求时间) + tcp关闭 + 2MSL。红色加粗表示每一次请求发送时间、每一次请求脚本执行时间、每一次响应发送时间,还有两两请求相隔时间。进一步测试,正在关闭或者TIME_WAIT状态的tcp链接,不能传输http请求和响应。即,当一个链接结束keepalive_timeout计时,服务端守护进程发送第一个FIN标志ip包后,该链接不能再使用了。

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    echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
2    echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
3    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分钟足够了。

也就是说,仅当nginx的keepalive_timeout值设置高于tcp_keepalive_time,而且距此tcp链接传输的最后一个http响应,通过了tcp_keepalive_time时间以后,操做系统才会发送侦测包来决定是否要丢弃这个TCP链接。通常不会出现这种状况,除非你须要这样作。

keep-alive与TIME_WAIT

使用http keep-alvie,能够减小服务端TIME_WAIT数量(由于由服务端httpd守护进程主动关闭链接)。道理很简单,相较而言,启用keep-alive,创建的tcp链接更少了,天然要被关闭的tcp链接也相应更少了。

最后

我想用一张示意图片来讲明使用启用keepalive的不一样。另外,http keepalive是客户端浏览器与服务端httpd守护进程协做的结果,因此,咱们另外安排篇幅介绍不一样浏览器的各类状况对keepalive的利用。

 
Keep-Alive模式,客户端如何判断请求所获得的响应数据已经接收完成(或者说如何知道服务器已经发生完了数据)?
1. 使用消息首部字段Conent-Length

故名思意,Conent-Length表示实体内容长度,客户端(服务器)能够根据这个值来判断数据是否接收完成。可是若是消息中没有Conent-Length,那该如何来判断呢?又在什么状况下会没有Conent-Length呢?请继续往下看……

相关文章
相关标签/搜索