HTTP通信是由TCP/IP承载的,HTTP紧挨着TCP,位于其上层,所以HTTP事务的性能很大程度上取决于底层TCP通道的性能。
HTTP事务的时延
如图:
HTTP事务的时延有以下几种主要原因。
(1)客户端首先需要根据URI确定Web服务器的IP地址和端口号。如果最近没有对URI中的主机名进行访问,通过DNS解析系统将URI中的主机名转换成一个IP地址可能要花费数十秒的时间。
(2)接下来,客户端会向服务器发送一条TCP连接请求,并等待服务器回送一个请求接受应答。每条新的TCP连接都会有连接建立时延。这个值通常最多只有一两秒种,但如果有数百个HTTP事务的话,这个值会快速地叠加上去。
(3)一旦连接建立起来了,客户端就会通过新建立的TCP管道来发送HTTP请求。数据到达时,Web服务器会从TCP连接中读取请求报文,并对请求进行处理。因特网传输请求报文,以及服务器处理请求报文都需要时间。
(4)然后,Web服务器会回送HTTP响应,这也需要花费时间。
这些TCP网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。TCP协议的技术复杂性也会对时延产生巨大的影响。
性能聚焦区域
会对HTTP产生影响、最常见的TCP相关时延包括:
(1)TCP连接建立握手:HTTP事务越小,TCP连接建立握手所花时间占的比例就越大。
(2)TCP慢启动拥塞控制:TCP连接会随着时间进行自我“调谐”,起初会限制连接的最大速度,如果数据传输成功,会随着时间的推移提高传输速度,主要用于防止因特网的突然过载和拥塞。由于存在这种拥塞控制特性,所以新连接的传输速度会比已经交换过一定量数据的、“已调谐”连接慢一些。
(3)数据聚集的Nagle算法:TCP有一个数据流接口,应用程序可以通过它将任意尺寸的数据放入TCP栈中——即使一次只放一个字节也可以。所以如果TCP发送了大量包含少数数据的分组,网络的性能就会严重下降。Nagle算法试图在发送一个分组前,将大量TCP数据绑定在一起,以提高网络效率。这个算法会引发几种HTTP性能问题。首先,小的HTTP报文可能无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延。其次,Nagle算法与延迟确认之间的交互存在问题——Nagle算法会阻止数据的发送,直到有确认的分组抵达为止,但确认分组自身会被延迟确认算法延迟100~200毫秒。HTTP应用程序常常会在自己的栈中设置参数TCP_NODEALY,禁止Nagle算法,提高性能。如果要这么做的话,一定要确保会向TCP写入大块的数据,这样就不会产生一堆小分组。
(4)用于捎带确认的TCP延迟确认算法:每个TCP段都有一个序列号和数据完整性校验和。每个段的接收者收到完整的段时,都会向发送者回送小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或损毁,并重发数据。由于确认报文很小,所以TCP允许在发往相同方向的输出数据分组中对其进行“捎带”。为了增加确认报文找到同向传输数据分组的可能性,很多TCP栈都实现了一种“延迟确认”算法。延迟确认算法会在一个特定的窗口时间(通常是100~200毫秒)内将输出确认存放在缓冲区中,以录找能够捎带它的输出数据分组。如果在那个时间段内没有输出数据分组,就将确认信息放在单独的分组中传送。但是HTTP具有双峰特征的请求——应答行为降低了捎带信息的可能。当希望有相反方向回传分组的时候,偏偏没有那么多。通常,延迟确认算法会引入相当大的时延。根据所使用操作系统的不同,可以调整或禁止延迟确认算法。
(5)TIME_WAIT时延和端口耗尽:当某个TCP端点关闭TCP连接时,会在内存中维护一个小的控制块,用来记录最近所关闭连接的IP地址和端口号。这类信息只会维持一小段时间,通常是所估计的最大分端使用期的两倍(称为2MSL,通常为2分钟)左右,以确保在这段时间内不会创建具有相同地址和端口号的新连接。实际上这个算法可以防止在两分钟内创建、关闭并重新创建两个具有相同IP 地址和端口号的连接。由于可用源端口的数量有限(比如60000个),而且在2MSL秒(比如120秒)内连接是无法重用的,连接速率就被限定在了60000/120 = 500 次/秒。超过这个数字,就会因端口耗尽而失败。要修正这个问题,可以把2MSL设置为一个较小的值,或增加客户端负载生成机器的数量,或确保客户端和服务器在循环使用几个虚拟IP 地址以增加更多的连接组合。