什么是HTTPKeep-Alive呢?

什么是HTTPKeep-Alive呢?

2023年7月23日发(作者:)

什么是HTTPKeep-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()调⽤)。但是,并不是免费的午餐,长时间的tcp连接容易导致系统资源⽆效占⽤。配置不当的keep-alive,有时⽐重复利⽤连接带来的损失还更⼤。所以,正确地设置keep-alive timeout时间⾮常重要。keepalvie timeoutHttpd守护进程,⼀般都提供了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 "";1. 当keepalive_timeout时间为0时,即不启⽤Keep-Alive时,⼀个tcp连接的⽣命周期:01 #tcpdump -n host 218.1.57.236 and port 8002 20:36:50.792731 IP 218.1.57.236.43052 > : S 1520902589:1520902589(0) win 6553503 20:36:50.792798 IP > 218.1.57.236.43052: S 290378256:290378256(0) ack 1520902590 win 584004 20:36:50.801629 IP 218.1.57.236.43052 > : . ack 1 win 3276805

06 20:36:50.801838 IP 218.1.57.236.43052 > : P 1:797(796) ack 1 win 3276807 20:36:50.801843 IP > 218.1.57.236.43052: . ack 797 win 5908

09 20:37:50.803230 IP > 218.1.57.236.43052: P 1:287(286) ack 797 win 5910 20:37:50.803289 IP > 218.1.57.236.43052: F 287:287(0) ack 797 win 5911 20:37:50.893396 IP 218.1.57.236.43052 > : . ack 288 win 3262512 20:37:50.894249 IP 218.1.57.236.43052 > : F 797:797(0) ack 288 win 3262513 20:37:50.894252 IP > 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设置为300s01 #tcpdump -n host 218.1.57.236 and port 8002 21:38:05.471129 IP 218.1.57.236.54049 > : S 1669618600:1669618600(0) win 6553503 21:38:05.471140 IP > 218.1.57.236.54049: S 4166993862:4166993862(0) ack 1669618601 win 584004 21:38:05.481731 IP 218.1.57.236.54049 > : . ack 1 win 3276805 21:38:05.481976 IP 218.1.57.236.54049 > : P 1:797(796) ack 1 win 3276806 21:38:05.481985 IP > 218.1.57.236.54049: . ack 797 win 5907

08 21:38:07.483626 IP > 218.1.57.236.54049: P 1:326(325) ack 797 win 5909 21:38:07.747614 IP 218.1.57.236.54049 > : . ack 326 win 3260510 21:43:07.448454 IP > 218.1.57.236.54049: F 326:326(0) ack 797 win 5911 21:43:07.560316 IP 218.1.57.236.54049 > : . ack 327 win 3260512 21:43:11.759102 IP 218.1.57.236.54049 > : F 797:797(0) ack 327 win 3260513 21:43:11.759111 IP > 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 8002 22:43:57.102448 IP 218.1.57.236.49955 > : S 4009392741:4009392741(0) win 6553503 22:43:57.102527 IP > 218.1.57.236.49955: S 4036426778:4036426778(0) ack 4009392742 win 584004 22:43:57.111337 IP 218.1.57.236.49955 > : . ack 1 win 3276805

06 22:43:57.111522 IP 218.1.57.236.49955 > : P 1:797(796) ack 1 win 3276807 22:43:57.111530 IP > 218.1.57.236.49955: . ack 797 win 5908 22:43:59.114663 IP > 218.1.57.236.49955: P 1:326(325) ack 797 win 5909 22:43:59.350143 IP 218.1.57.236.49955 > : . ack 326 win 3260510

11 22:45:59.226102 IP 218.1.57.236.49955 > : P 1593:2389(796) ack 650 win 3244312 22:45:59.226109 IP > 218.1.57.236.49955: . ack 2389 win 8313 22:46:01.227187 IP > 218.1.57.236.49955: P 650:974(324) ack 2389 win 8314 22:46:01.450364 IP 218.1.57.236.49955 > : . ack 974 win 3228115

16 22:47:57.377707 IP 218.1.57.236.49955 > : P 3185:3981(796) ack 1298 win 3211917 22:47:57.377714 IP > 218.1.57.236.49955: . ack 3981 win 10818 22:47:59.379496 IP > 218.1.57.236.49955: P 1298:1622(324) ack 3981 win 10819 22:47:59.628964 IP 218.1.57.236.49955 > : . ack 1622 win 3276820

21 22:50:59.358537 IP > 218.1.57.236.49955: F 1622:1622(0) ack 3981 win 10822 22:50:59.367911 IP 218.1.57.236.49955 > : . ack 1623 win 3276823 22:50:59.686527 IP 218.1.57.236.49955 > : F 3981:3981(0) ack 1623 win 3276824 22:50:59.686531 IP > 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-alivehttp 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_time2 echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl3 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 CRLFhex-no-zero = 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组成。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1690103879a306167.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信