导读:最近看完了大部头著作《HTTP权威指南》,对于此类指南类、手册类图书,往往让我们联想到的就是枯燥无味的使用讲解、技术指标讲解......使人头大。但是这本书却让我觉得读起来很“清新”,一方面作者用了浅显易懂的语言和大量的图示让我们很容易知所以然,另一方面应该是我一直以来对网络编程的兴趣和此书的内容有很大的契合点,今天要讲的内容也是与自己的兴趣有关的HTTP协议中有关TCP的部分,是从书中第四章——”连接管理“的部分内容总结而来。


HTTP请求过程中会有哪些网络时延?

wKiom1UhMG2hoBaIAACFJLHKC_U641.jpg

  1. 域名解析:域名解析是进行网络访问的第一步,把域名识别为TCP认识的IP地址。这步往往会因为域名解析服务的质量造成诸多问题,我在实际的工程实践中遇到的最常见的问题就是选择的域名服务商质量不好或者客户端本身设置的域名解析服务地址错误导致域名解析慢或者失败。不过现在对于大多数的HTTP客户端都有一个小的DNS缓存,用来保存近期所访问站点的IP地址,可以有效的缓解此问题。

  2. 接下来,客户端会向服务器发送一条TCP连接请求,并等待服务器回送一条响应。每条新的TCP连接都有连接建立时延(通常最多只有一两秒钟),对于单线程浏览器而言,如果有数百个并发的HTTP事务的话,可想而知时间叠加值也会很大。

  3. 一旦连接建立起来,客户端和服务器端就会通过这个建立的TCP管道来进行请求的收发了,这些TCP的网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。请参见我的文章《构建高性能服务的考量》

TCP相关时延
  • TCP连接建立握手

    wKioL1UhMc3RLTTHAACWKdyGkGs865.jpg
    建立一条新的TCP连接时,TCP两端之间会交换一系列的IP分组,如果连接只是用来传送少量的数据,相比而这种建立连接的过程会大大影响HTTP的性能。

    通常HTTP的事务都不会交换太多数据,SYN/SYN+ACK两次握手会产生一个可测量的时延,但第三次握手——TCP连接ACK分组通常都足够大,可以承载整个HTTP的请求报文(现代的TCP协议栈都允许客户端在这个确认分组中发送数据),而且很多HTTP服务器响应报文都可以放到一个IP分组中去。

    可以看出,小的HTTP事务可能会在TCP建立连接上花费50%的时间之多。所以我们现实中常常会有重用HTTP连接的需求。

  • TCP的延迟确认机制
    我们都知道因特网自身是无法保证可靠的分组传输的,所以TCP实现了自己的确认机制来确保数据的传输成功。在这种确认机制中使用的就是确认报文,由于确认报文很小,于是TCP将要返回的确认信息与输出的数据分组结合在一起发送可以更有效的利用网络。为了增加确认报文找到同向传输数据分组的可能性,很多TCP协议栈都实现了一种“延迟确认”算法——在一个特定的窗口时间(通常是100-200毫秒)内将输出确认放在缓冲区中,以寻找能够捎带它的输出数据分组,否则超出这个时段就将确认数据单独发送。

    但是HTTP具有双峰特征的请求——应答行为降低了捎带信息的可能。当希望有相反方向回传分组的时候,偏偏没有那么多。通常,延迟确认算法会引入相当大的时延,所以我们应该依据相应的操作系统的不同调整或者禁用延迟确认算法。

注:在对TCP协议栈的任何参数进行修改之前,一定要对自己做什么有清醒的认识。TCP中引入这些算法的目的是防止设计欠佳的应用程序对网络造成破坏。所以修改这些配置后都应该保证应用程序不会引发这些算法所要避免的问题。

  • TCP慢启动(拥塞控制)
    为了更好的保护网络,TCP慢启动限制了一个TCP端点在任意时刻可以传输的分组数。TCP数据传输起初会限制传输速度(传输分组数),如果数据成功传输,会随着时间的推移提高传输速度(传输分组数)。如果某个HTTP事务有大量数据要发送,是不能一次将所有分组都发送出去的。必须依赖慢启动逐渐的打开拥塞窗口。

    由于存在这种拥塞控制特性,所以新连接的传输速度会比已经交换过一定量数据的连接慢一些。这样又需要我们从重用HTTP连接(持久连接)的角度去考虑提高传输性能。

  • Nagle算法与TCP_NODELAY
    如果TCP连接总是发送大量包含少量数据的分组,网络的性能就会严重下降。Nagle算法就是试图在发送一个分组之前,将大量TCP数据绑定在一起发送(鼓励发送全尺寸的段,比如以太网上的段大小是1500字节,否则就进行缓存,要么当所有其他分组都被确认之后,Nagle算法才允许发送非全尺寸的分组),以提高网络效率。

    Nagle算法会引发几种HTTP的性能问题。比如小的HTTP报文可能无法填满一个分组,所以要缓存等待起来,要么就等待确认分组的抵达(确认分组的时延大概在100-200毫秒)。

    所以HTTP应用程序常常会在自己的协议栈中设置参数TCP_NODELAY,禁用Nagle算法来提高性能。

  • TIME_WAIT累积与端口耗尽
    关于TIME_WAIT状态的解释请看我的这篇博文
    《网络编程释疑之:TCP的TIME_WAIT状态在服务器开发中的影响?》,之前TIME_WAIT时间的设置为2分种之多是因为早期路由器的处理速度还比较慢,但是现在高速路由器的使用已经大大弱化了这个问题,所以对于web服务器来说可以通过操作系统设置来减小TIME_WAIT状态的持续时间,否则如果服务器存在大量的TIME_WAIT状态会大大影响服务器的性能。至于端口耗尽的情况则是针对少量客户端主机对web服务器进行基准测试时可能出现的问题,与TCP连接四元组有关。

参考书籍:

《HTTP权威指南》