我们的 Web 页面通常有很多对像(Object)组成。如:jss 样式表、图片、scripts、文档等。所以用户浏览一个网页文件时候,要向 Web 服务器发送多次请求(要从服务器上获取一个Object就要向服务器发送一个请求),浏览器根据 jss 样式表把从服务器获取的这些html页面对象合成一个完整的html页面展示给用户。
最早我们的浏览器是单线程的,意味着一次只能向浏览器发送一个Object请求,等到该Object传输完成了,再向服务发送第二个Object的请求。我们把它称为串行事务处理。串行事务处理,使得我们的连接时延会叠加,用户的体验效果差。如,页面有多幅图片,页面正在加载一幅图片时,页面上其它地方都没有动静,也会让人门觉得很慢。后来出现了多线程的浏览器,当用户点击打开一个页面时,会同时向服务器同时发起多个用户请求(也就是并行处理方式),减少了连接时延叠加,同时加速了一个web页面对象的加载速度,让用户有更好的体验效果。
虽然采用多线程的浏览器加速了页面的加载速度,但是如果我们只对连接进行简单的管理(如不使用 keep alive),浏览器每获得一个Web对像都要使用一个新的TCP连接。
意思是说我们加载的html页面有多少个页面对象,浏览器与服务器要建立多少条TCP连接。大家都知道使用TCP传输数据之前,要先经过三次握手,三次握手成功以后,双方才能够进行数据的传输。
所以说,我们使用TCP/IP进行数据网络传输必定会造成延迟的。双方完成数据的传输以后还要经过TCP的四次断开的过程。一个TCP的连接要经过:建立连接 、传输数据、拆除连接。
TCP的建立连接和拆除连接是很费时的,有时候甚至比数据传输的时间还长。所以,虽然浏览器采用了并发处理方式,加速了页面的加载速度。但是请求一个页面对像就需要与服务器建立一条TCP连接。如果用户浏览的页面文件有1000个object的话,从服务器请求数据到展示给用户,
最基本延迟时间 = 1000*(平均每个TCP连接建立时间 + 平均每个TCP连接拆除时间)。
随着我们的页面对像的增加,这个延迟时间是不断增长的。客户端每请求一个object,就要与服务器建立一条TCP连接,服务器每维护一条TCP连接是要消耗一定的资源(如内存)。所以,也加速了服务器的负担。对服务器的并发用户数也造成很大影响。所以后来 HTTP/1.1 使用了重用TCP连接功能来消除连接及关闭时延。允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,
以便为后续的HTTP请求重用现存的TCP连接。在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。也称为 TCP 重用。
是如何重用TCP连接的呢?
假如,浏览的网页文件有400个object.我们的浏览器是4线程的,浏览器会并行向 Web 服务器发送4个 TCP连接请求。当这4个TCP请求与服务器建立连接完成数据传输以后,并不是
把它拆除掉。浏览器与web服务器协定使用 keep-alive 功能时。HTTP设备就会在事务处理结束之后将该4条TCP连接保持在打开状态。浏览器就使用这4条TCP连接完成后续的396个object的数据转输。
持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而且减少了打开连接的潜在数量。但是,管理 持久连接时要特别小心,不然就会累积出大量的空闲连接,耗费客户端和服务器上的资源。下面是 Apache web 服务器管理持久连接的一些配置:
[root@node2 ~]# vim /etc/httpd/extra/httpd-default.conf ... #KeepAlive On KeepAlive On # # MaxKeepAliveRequests: The maximum number of requests to allow # during a persistent connection. Set to 0 to allow an unlimited amount. # We recommend you leave this number high, for maximum performance. # 保持连接允许传输的最大请求数 MaxKeepAliveRequests 100 # KeepAliveTimeout: Number of seconds to wait for the next request from the # same client on the same connection. # 在同一个客户端的连接,等待下一个请求的超时时间 KeepAliveTimeout 5 ...
说明:
这些就是 Keep-Alive选项。
注意,Keep-Alive 首部只是请求将连接保持在活跃状态。发出 keep-alive 请求之后,客户端和服务器并不一定会同意进行 keep-alive 会话。
它们可以在任意时刻关半空闲的 keep-alive 连接,并可随意限制 keep-alive 连接所处理事务的数量。
下面来看看,客户端与服务器怎样商量它们是否使用HTTP协议的持久连接功能的呢?
实现 HTTP/1.0 keep-alive 连接的客户端可以通过包含 Connection: Keep-Alive 首部请求将一条连接保持在打开状态。
通过 Google Chrome 浏览器的开发者工具来查看,访问 http://192.168.203.99/index.html 的请求头信息。
Request Header Accept:text/html,application/xhtml+xml,application/xml;q=0.9,p_w_picpath/webp,*/*;q=0.8 Accept-Encoding:gzip,deflate,sdch Accept-Language:zh-CN,zh;q=0.8 Cache-Control:no-cache Connection:keep-alive -----> 请求将一条连接保持在打开状态。 Cookie:2c407_ol_offset=97; 2c407_ipstate=1402781651; 2c407_jobpop=0; 2c407_winduser=BD4OUFQKBQkLVgReBgsAAFsDVlMKB1MGUQ4LAwcFUlgBBms; 2c407_ck_info=%2F%09; 2c407_lastpos=index; 2c407_lastvisit=49%091402713397%09%2Findex.php Host:192.168.203.99 Pragma:no-cache User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36
通过工具 crul 获得的响应头信息。
[root@node2 ~]# curl -I http://192.168.203.99/index.html HTTP/1.1 200 OK Server: nginx/1.0.11 Date: Sat, 14 Jun 2014 09:17:15 GMT Content-Type: text/html Content-Length: 151 Last-Modified: Thu, 01 May 2014 04:21:03 GMT Connection: keep-alive Accept-Ranges: bytes
说明:
如果服务器愿意为下一条请求将连接保持在打开状态(意思是说下一次请求数据时,可以通过该TCP连接传输数据,不需要建立新的TCP连接了),
就在响应中包含相同的首部 Connection: keep-alive。
如果响应中没有 Connection: keep-alive 首部,客户端就认为服务器不支持 keep-alive,会在发回响应报文之后关闭连接@。
从上在请求首部和响应首部分析,我们使用了HTTP 持久连接的功能。
总结:
Keep-Alive 连接的限制和规则:
1、在 HTTP/1.0 中,keep-alive 并不是默认使用的,客户端必须发送一个 Connection: Keep-Alive
请求首部来激活 keep-alive 连接。
2、Connection: Keep-Alive 首部必须随所有希望保持持久连接的报文一起发送。如果客户端没有发
送 Connection: Keep-Alive 首部,服务器就会在那条请求之后关闭连接。
3、客户端探明响应中没有 Connection: Keep-Alive 响应首部,就可以知道服务器发出响应之后是否
会关闭连接了。
4、为了避免出现大量的空闲的TCP连接,要定义持久连接的超时时间 timeout. 限制操持连接的TCP
连接最多能完成多少个事务 MaxKeepAliveRequests