TCP与HTTP连接管理

HTTP事务时延原因(HTTP权威指南 P86)

  1. 客户端首先需要根据URI确定WEB服务器的IP和端口号, 那么DNS解析上花的时间会很多(大多数HTTP客户端会有一个小的DNS缓存)

  2. 客户端会向服务器发送建立TCP连接请求, 每个请求建立会耗时间, 如果HTTP事务多的话, 这个时间会明显提高

  3. 一旦连接建立, 客户端会通过此TCP管道来发送HTTP报文, 服务端会接收并读取处理, 这些都会消耗时间

  4. WEB服务器会回送HTTP响应, 这也需要时间

这些TCP网络延迟的大小取决于硬件速度, 网络和服务器负载, 请求和响应报文的尺寸, 客户端和服务器之间的距离, TCP协议的技术复杂度也会对时延有巨大的影响


性能聚焦区域

1. TCP连接的握手时延:

TCP连接握手步骤:

a. 请求新的TCP连接时, 客户端需要向服务器发送一个小的TCP分组(通常是40-60字节), 这个分组设置了一个特殊的SYN标记. 说明这是一个连接请求

b. 如果服务器接受了连接, 就会对一些连接参数进行计算, 并向客户端回送一个TCP分组, 这个分组的SYN和ACK标记都被置位, 说明连接请求已被建立

c. 最后, 客户端向服务器会送一条确认信息, 通知它连接已成功建立, 现代的TCP栈都允许客户端在这个确认分组中发送数据

结论是: 小的HTTP事务可能会在TCP建立上花费50%, 或更多的时间, HTTP可以通过重用现存连接减少建立连接的时间

2. 延迟确认

由于因特网自身无法确保可靠的分组传输, TCP实现了自己的确认机制来确保数据的成功传输

每一个TCP段都会有一个序列号和数据完整性校验和, 每个段的接受者收到完好的信息时, 会想发送者回送小的确认分组, 如果发送者没有在指定的窗口时间收到确认信息, 发送者就认为这个分组已被破坏, 并重发数据

由于确认报文很小, 所以TCP允许再发往相同方向的输出数据分组中对其进行捎带. TCP将返回的确认信息与输出的数据分组结合在一起, 可以更有效的利用网络. 为了增加确认报文找到同向传输数据分组的可能性, 很多TCP栈都实现了”延迟确认”算法. 延迟确认算法会在一个特定的窗口时间(100-200ms) 将确认信息放在缓冲区内, 以寻找能够捎带它的输出数据分组, 如果窗口时间内没有输出数据, 则独立发送.

但是, http具有双峰特征的请求, 应答行为降低了捎带信息的可能. 当希望有相反方向回传数据的时候, 没有那么多, 所以延迟确认算法会有相当大的延迟, 所以可以调整或禁止延迟确认算法 在对TCP栈进行参数修改前, 需要对此有清楚的认识. TCP的这些算法是为了防止设计欠佳的应用对因特网的破坏. 对TCP进行的修改, 需要确保应用程序不会引发这些算法要避免的问题

3. TCP慢启动

TCP数据传输性能还取决于TCP连接的使用期(age). TCP连接会随着时间的进行自我”调谐”, 起初会限制连接的最大速度, 如果数据成功传输, 会随着时间的推移提高传输速度, 这种调谐成为TCP慢启动, 用于防止因特网的突然过载和拥塞

TCP的慢启动限制了一个TCP端点在任一时刻可以传输的分组数, 简单的说, 每成功接受一个分组, 发送端就有一个发送另外两个分组的权限. 如果某个http事务有大量的数据需要发送, 是不能一次发送所有分组数据的, 必须发送一个分组, 等待确认, 然后可以发送两个分组, 每个分组都必须被确认, 这样就可以发送四个分组. 以此类推, 这种方式被称为”打开拥塞窗口”

由于存在这种拥塞控制特性. 所以新连接的传输速度会比已经交换过一些数据的”已调谐”的连接慢一些, 由于已调谐的连接更快些, 所以HTTP中有一些可以重用现存连接的工具

4. Nagle算法和TCP_NODELAY

TCP有一个数据流接口, 应用程序可以通过它将任意尺寸的数据放入TCP栈–一次只放一个字节也是可以的! 但是每一个TCP段都至少装载了40个字节的标记和首部, 所以如果TCP发送了大量包含少量数据的分组, 网络的性能就会严重下降.

Nagle算法试图发送一个分组之前, 将大的TCP数据绑定在一起, 以提高网络效率. Nagle算法鼓励发送全尺寸的数据段.LAN上1500字节, 因特网为几百字节. 只有当所有分组都被确认之后, Nagel算法才允许发送非全尺寸的数据, 如果其他分组仍在传输过程中, 就将那部分数据缓存起来, 只有当挂起分组被确认, 或者缓存区中积累了足够发送一个全尺寸的分组数据时, 才将缓存发出去

Nagel算法会引起几个HTTP性能问题, 首先, 小的HTTP报文可能无法填满一个分组, 可能会因为等待那些永远不会到来的额外数据而产生时延. 其次, Nagel算法与延迟确认之间的交互存在问题–Nagle算法会阻止数据的发送, 直到有分组抵达为止, 但是分组自身确认时间100-200ms

HTTP程序常常会在自己的栈中设置参数 TCP_NODELAY 来禁用nagel算法, 但是要确保向TCP写入大块数据吗这样就不会产生一堆小分组了

5. TIME_WAIT累积和端口耗尽

这个问题值得关注, 当某个TCP端点关闭TCP连接时, 会在内存中维护一个小的控制块, 用来记录最近所关闭连接的IP地址和端口号, 这类信息只维护一段时间(大约两分钟), 以确保这段时间内不会创建具有相同地址和端口号的连接, 实际上, 这个算法可以防止两分钟内创建, 关闭并重新创建两个具有相同IP地址和端口号的连接

2Min的连接关闭延迟通常不是什么问题, 但是性能基准环境下就可能会成为一个问题, 进行性能基准测试时, 通常只有一台或几台用来产生流量的计算机连接到某系统中去, 这样就限制了连接到服务器的客户端IP地址数, 而且, 服务器通常会在HTTP默认TCP端口80上进行监听, 用TIME_WAIT防止端口号重用时, 这些情况也限制了可用的连接值组合,

只有一个客户端和一台服务器的异常情况下, 构建一条TCP连接的4个值, 其中3个都是固定的, 只有源端口号可以随意改变. 每次客户端连接到服务器上去时, 都会获得新的源端口, 但由于源端口可用数量有限, 即使没有遇到端口耗尽问题, 也要特别注意大量处理打开状态的情况