一. 关于HTTP
首先,HTTP是超文本传输协议,是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,其主要特点有如下:
- 支持客户/服务器模式;
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快;
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记;
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间;
- 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快
二. keep-alive
既然上面提到了HTTP是基于请求与响应的,且最主要的两个特点就是无连接和无状态,但需要说明的是,虽然是无连接的,但其底层也就是传输层大多却是基于
TCP面向连接的通信方式,因此,这里的无连接指的是:当server端和client端进行通讯的时候,client端向server端发起请
求,server端接收请求之后返回给client端一个响应,之后就会断开不再继续保持连接了;这样有一个好处就是对于只有一次访问的连接来说不仅节省
资源还很高效,但很明显,如果client端还想继续多次访问server端就需要重新建立连接也就是会多次进行TCP的“三次握手,四次挥手”的过程,
这样一来并没有节省资源而且还很低效,因此,使用keep-alive(又称持久连接、连接重用)可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,避免了建立或者重新建立连接的次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的TCP连接意味着更少的系统内核调用,socket的accept()和close()调用)。
HTTP 1.0
中keep-alive默认是关闭的,需要在HTTP头加入"Connection: Keep-Alive",才能启用Keep-Alive;HTTP
1.1中默认启用Keep-Alive,如果加入"Connection: close ",才关闭。目前大部分浏览器都是用HTTP
1.1协议,也就是说默认都会发起Keep-Alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看服务器设置情况。
其中,RFC 2616 (P47)指出:单用户客户端与任何服务器或代理之间的连接数不应该超过2个。一个代理与其它服务器或代码之间应该使用不超过2 * N的活跃并发连接。这是为了提高HTTP响应时间,避免拥塞(冗余的连接并不能代码执行性能的提升)。
三. keep-alive timeout时间
keep-alive并不是免费的午餐,长时间的TCP连接容易导致系统资源无效占用,配置不当的keep-alive 有时比重复利用连接带来的损失还更大;因此,正确地设置keep-alive timeout时间非常重要。
httpd守护进程一般都提供了keep-alive
timeout时间设置参数,比如nginx的keepalive_timeout和Apache的keepalivetimeout。这个
keepalive_timeout时间值意味着:一个http产生的TCP连接在传送完最后一个响应后,还需要保持keepalive_timeout
时间后才开始关闭这个连接;
在没有设置keepalive_timeout的情况下,一个socket资源从建立到真正释放所需要经过的时间是:建立TCP连接(三次握手)+传送http请求+脚本指向+传送http响应+关闭TCP连接(四次挥手)+主动关闭的一方进入TIME_WAIT的2MSL等待时间;
当设定了keepalive_timeout时间之后,一个socket由建立到释放所需要经过的时间是:TCP建立连接(三次握手)+(最后一次响应 - 第一次请求时间)+TCP关闭连接(四次挥手)+2MSL;
也就是说,当使用keep-alive机制的时候,当一次请求-响应结束之后,这个连接还会继续维持上keepalive_timeout时间,如果在这
个时间内client端还有请求发过来,那么server端会继续处理给予响应,如果keepalive_timeout时间计时结束后,就会进入TCP
释放连接的阶段,因此也就会结束掉这次通信;
四. keep-alive模式的使用
虽然keep-alive模式可以降低TCP连接的次数提高效率,但并不是什么情况下都适合使用keep-alive机制的,如下举个栗子:
比如很多网页中图片、CSS、JS、Html都在一台Server上,当用户访问其中的Html网页时,网页中的图片、Css、Js都构成了访问请求,打开KeepAlive属性可以有效地降低TCP握手的次数(当然浏览器对同一域下同时请求的图片数有限制,一般是2),减少httpd进程数,从而降低内存的使用(假定prefork模式)。MaxKeepAliveRequests和KeepAliveTimeOut两个属性在KeepAlive=On时起作用,可以控制持久连接的生存时间和最大服务请求数。
不过,上面说的只是一种情形,那就是静态网页居多的情况下,并且网页中的其他请求与网页在同一台Server上。当你的应用动态程序(比如:php)居多,用户访问时由动态程序即时生成html内容,html内容中图片素材和Css、Js等比较少或者散列在其他Server上时,KeepAlive=On反而会降低Apache的性能。为什么呢?
前面提到过,KeepAlive=On时,每次用户访问,打开一个TCP连接,Apache都会保持该连接一段时间,以便该连接能连续为同一client服务,在KeepAliveTimeOut还没到期并且MaxKeepAliveRequests还没到阈值之前,Apache必然要有一个httpd进程来维持该连接,httpd进程不是廉价的,他要消耗内存和CPU时间片的。假如当前Apache每秒响应100个用户访问,KeepAliveTimeOut=5,此时httpd进程数就是100*5=500个(prefork模式),一个httpd进程消耗5M内存的话,就是500*5M=2500M=2.5G,夸张吧?当然,Apache与Client只进行了100次TCP连接。如果你的内存够大,系统负载不会太高,如果你的内存小于2.5G,就会用到Swap,频繁的Swap切换会加重CPU的Load。
现在我们关掉KeepAlive,Apache仍然每秒响应100个用户访问,因为我们将图片、js、css等分离出去了,每次访问只有1个request,此时httpd的进程数是100*1=100个,使用内存100*5M=500M,此时Apache与Client也是进行了100次TCP连接。性能却提升了太多。
因此,总结:
- 当你的Server内存充足时,KeepAlive=On还是Off对系统性能影响不大;
- 当你的Server上静态网页(Html、图片、Css、Js)居多时,建议打开KeepAlive;
- 当你的Server多为动态请求(因为连接数据库,对文件系统访问较多),KeepAlive关掉,会节省一定的内存,节省的内存正好可以作为文件系统的Cache(vmstat命令中cache一列),降低I/O压力;
PS:当KeepAlive=On时,KeepAliveTimeOut的设置其实也是一个问题,设置的过短,会导致Apache频繁建立连接,给Cpu造成压力,设置的过长,系统中就会堆积无用的Http连接,消耗掉大量内存,具体设置多少,可以进行不断的调节,因你的网站浏览和服务器配置而异。