在通过调试工具查看网络请求的时候,通常在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连接。

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

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

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

    #tcpdump -n host 218.1.57.236 and port
::50.792731 IP 218.1.57.236. > 222.73.211.215.http: S :() win
::50.792798 IP 222.73.211.215.http > 218.1.57.236.: S :() ack win
::50.801629 IP 218.1.57.236. > 222.73.211.215.http: . ack win ::50.801838 IP 218.1.57.236. > 222.73.211.215.http: P :() ack win
::50.801843 IP 222.73.211.215.http > 218.1.57.236.: . ack win ::50.803230 IP 222.73.211.215.http > 218.1.57.236.: P :() ack win
::50.803289 IP 222.73.211.215.http > 218.1.57.236.: F :() ack win
::50.893396 IP 218.1.57.236. > 222.73.211.215.http: . ack win
::50.894249 IP 218.1.57.236. > 222.73.211.215.http: F :() ack win
::50.894252 IP 222.73.211.215.http > 218.1.57.236.: . ack win
  • 第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

    #tcpdump -n host 218.1.57.236 and port
::05.471129 IP 218.1.57.236. > 222.73.211.215.http: S :() win
::05.471140 IP 222.73.211.215.http > 218.1.57.236.: S :() ack win
::05.481731 IP 218.1.57.236. > 222.73.211.215.http: . ack win
::05.481976 IP 218.1.57.236. > 222.73.211.215.http: P :() ack win
::05.481985 IP 222.73.211.215.http > 218.1.57.236.: . ack win ::07.483626 IP 222.73.211.215.http > 218.1.57.236.: P :() ack win
::07.747614 IP 218.1.57.236. > 222.73.211.215.http: . ack win
::07.448454 IP 222.73.211.215.http > 218.1.57.236.: F :() ack win
::07.560316 IP 218.1.57.236. > 222.73.211.215.http: . ack win
::11.759102 IP 218.1.57.236. > 222.73.211.215.http: F :() ack win
::11.759111 IP 222.73.211.215.http > 218.1.57.236.: . ack win
   
  • 我们先看一下,第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个请求。

    # tcpdump -n host 218.1.57.236 and port
::57.102448 IP 218.1.57.236. > 222.73.211.215.http: S :() win
::57.102527 IP 222.73.211.215.http > 218.1.57.236.: S :() ack win
::57.111337 IP 218.1.57.236. > 222.73.211.215.http: . ack win ::57.111522 IP 218.1.57.236. > 222.73.211.215.http: P :() ack win
::57.111530 IP 222.73.211.215.http > 218.1.57.236.: . ack win
::59.114663 IP 222.73.211.215.http > 218.1.57.236.: P :() ack win
::59.350143 IP 218.1.57.236. > 222.73.211.215.http: . ack win ::59.226102 IP 218.1.57.236. > 222.73.211.215.http: P :() ack win
::59.226109 IP 222.73.211.215.http > 218.1.57.236.: . ack win
::01.227187 IP 222.73.211.215.http > 218.1.57.236.: P :() ack win
::01.450364 IP 218.1.57.236. > 222.73.211.215.http: . ack win ::57.377707 IP 218.1.57.236. > 222.73.211.215.http: P :() ack win
::57.377714 IP 222.73.211.215.http > 218.1.57.236.: . ack win
::59.379496 IP 222.73.211.215.http > 218.1.57.236.: P :() ack win
::59.628964 IP 218.1.57.236. > 222.73.211.215.http: . ack win ::59.358537 IP 222.73.211.215.http > 218.1.57.236.: F :() ack win
::59.367911 IP 218.1.57.236. > 222.73.211.215.http: . ack win
::59.686527 IP 218.1.57.236. > 222.73.211.215.http: F :() ack win
::59.686531 IP 222.73.211.215.http > 218.1.57.236.: . ack win
  • 第一组,三个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保鲜定时器,支持三个系统内核配置参数:

    echo  > /proc/sys/net/ipv4/tcp_keepalive_time
echo > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo > /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组成。

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

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

    什么是looksalive check和is alive check SQL Server故障转移集群是建立在windows集群服务上的一种热备的高可用方案.在集群运行过程中,windows集群服务定 ...

  2. Keeping Async Methods Alive

    Consider a type that will print out a message when it’s finalized, and that has a Dispose method whi ...

  3. 转载:有关SQL server connection Keep Alive 的FAQ(3)

    转载:http://blogs.msdn.com/b/apgcdsd/archive/2012/06/07/sql-server-connection-keep-alive-faq-3.aspx 这个 ...

  4. 转载:有关SQL server connection Keep Alive 的FAQ(2)

    转: http://blogs.msdn.com/b/apgcdsd/archive/2012/05/18/sql-server-connection-keep-alive-faq-2.aspx 在下 ...

  5. Keep Alive

    跳板机时经常出现连接被断开的情况.如果发生这种情况,请在客户端配置Keep Alive设置,具体方法参考如下: Windows: secureCRT:Properties -> Terminal ...

  6. How to Keep Alive SSH Sessions

    How to Keep Alive SSH Sessions Many NAT firewalls time out idle sessions after a certain period of t ...

  7. keep alive的相关介绍

        无论Windows还是linux,Keepalive就三个配置参数.下文以linux环境为介绍. Technorati 标签: keepalive     tcp_sock结构体中有三个有关的 ...

  8. linux下socket keep alive讲解

    [需求] 不影响服务器处理的前提下,检测客户端程序是否被强制终了.[现状]服务器端和客户端的Socket都设定了keepalive属性.服务器端设定了探测次数等参数,客户端.服务器只是打开了keepa ...

  9. [Server Running] [Node.js, PM2] Using PM2 To Keep Your Node Apps Alive

    PM2 is a production process manager for Node.js applications with a built-in load balancer. It allow ...

  10. 【转】Linux下socket keep alive讲解

    [需求]不影响服务器处理的前提下,检测客户端程序是否被强制终了.[现状]服务器端和客户端的Socket都设定了keepalive属性.服务器端设定了探测次数等参数,客户端.服务器只是打开了keepal ...

随机推荐

  1. 怎样查询SCI和EI检索号

    为了年终考核,花了一个早上才搞清楚,里面有非常多小问题.以下具体说明具体过程: SCI检索号 1.进入图书馆主页: 2.选择"电子数据库": 3.选择外文数据库中的"We ...

  2. 随机函数(Pascal入门)

    随机函数是最主要的,在比赛的时候我们能够用随机函数来測试自己的程序是否会超时. 随机函数也能够做出一些大数据.用于两个程序之间对拍(一个是爆搜.一个是正解). 当然平时我们也能够用随机函数測自己的程序 ...

  3. 【Java基础】构造方法调用构造方法

    从一个程序开始: class dog { private String name; private String color; private int age; dog(String name) // ...

  4. Html5 Css实现方形图片 圆形显示

    <!doctype html><html><head><meta charset="utf-8"><title>方形图片 ...

  5. WLW 截屏插件

    转载:http://www.xtit.net/post/1030/ 一直以来用WLW更新博客,刚刚在DailyApps看到一个关于Windows Live Writer的截屏插件,相当不错. 是由MS ...

  6. JSP基础之 C标签中的 varStatues属性

    变量状态参数,该属性有5个常用值count 表示当前遍历集合的元素个数index 表示当前遍历到集合的第几个元素current 表示当前的集合元素first 表示集合的第一个元素last 表示集合的最 ...

  7. R - 一只小蜜蜂...(第二季水)

    Description          有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行.请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数.         其中,蜂房的结构如下所示.     ...

  8. B - Numbers That Count

    Description        "Kronecker's Knumbers" is a little company that manufactures plastic di ...

  9. 从汇编看c++的虚拟继承以及其内存布局(一)

    先看第一种最简单的情形,所有类中没有任何虚函数的菱形继承. 下面是c++源码: class Top {//虚基类 public: int i; Top(int ii) { i = ii; } }; c ...

  10. word保存时标题变成黑框(mac版本)

    参考:http://blog.sina.com.cn/s/blog_686020310101i2zu.html 参考文档中说的时windows版本的word,跟我mac处理方式有一些不同: word版 ...