什么是HTTP Keep-Alive呢?

在通过调试工具查看网络请求的时候,通常在response header能看到类似下面这种:Keep-Alive: timeout=10, max=94 。那么Keep-Alive到底是什么呢?

HTTP Keep-Alive

在http早期,每个http请求都要求打开一个tpc socket连接,并且使用一次之后就断开这个tcp连接。

使用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连接。

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

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脚本相符。
  • 第6、8行服务端发送http响应。发送响应用时90166μs。
  • 第7行,表明由服务端守护进程主动关闭连接。结合第6、8行,说明http响应一旦发送完毕,服务端马上关闭这个tcp连接
  • 第7、9、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呢?请继续往下看……

2.使用消息首部字段Transfer-Encoding

当客户端向服务器请求一个静态页面或者一张图片时,服务器可以很清楚的知道内容大小,然后通过Content-length消息首部字段告诉客户端需要接收多少数据。但是如果是动态页面等时,服务器是不可能预先知道内容大小,这时就可以使用Transfer-Encoding:chunk模式来传输数据了。即如果要一边产生数据,一边发给客户端,服务器就需要使用"Transfer-Encoding: chunked"这样的方式来代替Content-Length。

chunk编码将数据分成一块一块的发生。Chunked编码将使用若干个Chunk串连而成,由一个标明长度为0的chunk标示结束。每个Chunk分为头部和正文两部分,头部内容指定正文的字符总数(十六进制的数字)和数量单位(一般不写),正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。

Chunk编码的格式如下:

Chunked-Body = *chunk 
                                    "0" CRLF 
                                    footer 
                                    CRLF  
chunk = chunk-size [ chunk-ext ] CRLF 
                  chunk-data CRLF

hex-no-zero = <HEX excluding "0">

chunk-size = hex-no-zero *HEX 
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] ) 
chunk-ext-name = token 
chunk-ext-val = token | quoted-string 
chunk-data = chunk-size(OCTET)

footer = *entity-header

即Chunk编码由四部分组成:1、0至多个chunk块,2、"0" CRLF,3、footer,4、CRLF.而每个chunk块由:chunk-size、chunk-ext(可选)、CRLF、chunk-data、CRLF组成。

时间: 2024-08-30 15:10:48

什么是HTTP Keep-Alive呢?的相关文章

TCP 连接与TCP keep alive 保活检测机制

生产环境中一台2核4G的linux服务器TCP连接数时常保持在5-7w间徘徊,查看日志每秒的请求数也就100-200,怎么会产生这么大的TCP连接数.检查了下客户端上行的HTTP协议,Connection 头字段是Keep-Alive,并且客户端在请求完之后没有立即关闭连接.而服务端的设计也是根据客户端来的,客户端上行如果Connection:Keep-Alive,服务端是不会主动关闭连接的.在客户端与服务端交互比较频繁的时候,这样的设计还是比较合理的,可以减少TCP的重复握手.显然如果只交互一

Linux下关于TCP的keep alive的实现源码分析

TCP下的Keep Alive 我们常说的TCP的keep alive,就是为了保证连接的有效性,在间隔一定的时间发探测包,根据回复来确认该连接是否有效.通常上层应用会自己提供心跳检测机制,而Linux内核本身也提供了从内核层面的确保连接有效性的方式. 在sock 函数中可以设置是否需要打开keep alive开关,默认建立socket 是关闭keep alive的.代码如下 optval = 1; optlen = sizeof(optval); if(setsockopt(s, SOL_SO

【转】Linux下socket keep alive讲解

[需求]不影响服务器处理的前提下,检测客户端程序是否被强制终了.[现状]服务器端和客户端的Socket都设定了keepalive属性.服务器端设定了探测次数等参数,客户端.服务器只是打开了keepalive机能服务器端起了一个监视线程,利用select来检测socket是否被关闭... 下面这是我的一点肤浅理解. 1.关于keep alive 无论windows,还是linux,keepalive就三个参数: sk->keepalive_probes:探测次数sk->keepalive_tim

SQL Server 2012故障转移的looksalive check和is alive check

什么是looksalive check和is alive check SQL Server故障转移集群是建立在windows集群服务上的一种热备的高可用方案.在集群运行过程中,windows集群服务定期检测节点的资源健康状态,如果发生了故障,会根据预先定义的故障转移策略把SQL Server服务从故障节点切换到可用节点上,从而实现SQL Server的高可用. 而looksalive和isalive就是windows集群服务定期检测节点的资源健康状况的两个方法,它们存在于 resource dl

How to Keep Alive SSH Sessions

How to Keep Alive SSH Sessions Many NAT firewalls time out idle sessions after a certain period of time to keep their trunks clean. Sometimes the interval between session drops is 24 hours, but on many commodity firewalls, connections are killed afte

Linux 之Keep alive

基本介绍 Keep alive 可以设置在操作系统级别, 作用于对本机所建立的连接. 在设定的时间内对远程主机返送一个简单的tcp 包,用来探测远程主机是否还有响应. 主要应用场景有2个:1. 更早的知道远程主机down 掉了. 正常情况下A 主机 和B 主机建立了连接. A 发送了信息给B 主机,B 说收到了, A 开始等待. 但B 主机down 掉了, 但B 又没有对A 说自己已经down 了, 所以A 会一直等下去(除非应用程序设置的有timeout 机制).使用了keep alive 可

linux下socket keep alive讲解

[需求] 不影响服务器处理的前提下,检测客户端程序是否被强制终了.[现状]服务器端和客户端的Socket都设定了keepalive属性.服务器端设定了探测次数等参数,客户端.服务器只是打开了keepalive机能服务器端起了一个监视线程,利用select来检测socket是否被关闭... 下面这是我的一点肤浅理解. 1.关于keep alive 无论windows,还是linux,keepalive就三个参数: sk->keepalive_probes:探测次数sk->keepalive_ti

mha 复制检查报错“There is no alive server. We can&#39;t do failover”

安装mha所参考的文章: http://linzhijian.blog.51cto.com/1047212/1906434 http://www.cnblogs.com/xiaoboluo768/p/5984530.html 参考以上文章搭建mha0.57+centos7+mariadb10.1.22 配置文件内容: 验证: 1.验证ssh成功 2.验证复制状态失败 解决思路: 1.远程测试数据库是否可以连接,可以连接 答案:未解决 2.肯定不能度娘了 在谷歌上查询到wubx大师回答的如上错误的

keep alive的相关介绍

    无论Windows还是linux,Keepalive就三个配置参数.下文以linux环境为介绍. Technorati 标签: keepalive     tcp_sock结构体中有三个有关的成员变量.     keepalive_probes  : 探测次数     keepalive_time      : 探测的超时时间     keepalive_intvl       : 探测间隔     对 于一个已经建立的tcp连接.如果在keepalive_time时间内双方没有任何的数

python局域网alive ip侦听

python局域网alive ip侦听 作者:vpoet 日期:夏季 注:写着玩,欢迎copy # -*- coding: cp936 -*- # coding = utf-8 import os import re import thread import time import socket import sys def Ping_Ip(Curr_Ip): global Count_Thread,lock,ThreadsNum #print "*****************Chile_T