连接管理

4.1 TCP连接

4.1.2 TCP流是分段的、由IP分组传送
TCP 的数据是通过名为 IP 分组(或 IP 数据报)的小数据块来发送的

每个 TCP 段都是由 IP 分组承载,从一个 IP 地址发送到另一个 IP 地址的。每个 IP分组中都包括:
• 一个 IP 分组首部(通常为 20 字节);
• 一个 TCP 段首部(通常为 20 字节);
• 一个 TCP 数据块(0 个或多个字节)
4.1.3 保持TCP连接持续不断地运行
在任意时刻计算机都可以有几条 TCP 连接处于打开状态。TCP 是通过端口号来保持所有这些连接持续不断地运行
< 源 IP 地址、源端口号、目的 IP 地址、目的端口号 >

有 4 条连接:A、B、C 和 D。列出了每个端口的相关信息

 

 

 

注意,有些连接共享了相同的目的端口号(C 和 D 都使用目的端口号 80)。有些连接使用了相同的源 IP 地址(B 和 C)。有些使用了相同的目的 IP 地址(A 和 B,C
和 D)。但没有两个不同连接所有的 4 个值都一样。
4.2 对TCP性能的考虑
4.2.1 HTTP事务的时延

HTTP 事务的时延有以下几种主要原因。
(1) 客户端首先需要根据 URI 确定 Web 服务器的 IP 地址和端口号。如果最近没有对URI 中的主机名进行访问,通过 DNS 解析系统将 URI 中的主机名转换成一个 IP地址可能要花费数十秒的时间 3。
(2) 接下来,客户端会向服务器发送一条 TCP 连接请求,并等待服务器回送一个请求接受应答。每条新的 TCP 连接都会有连接建立时延。这个值通常最多只有一两秒钟,但如果有数百个 HTTP 事务的话,这个值会快速地叠加上去。
(3) 一旦连接建立起来了,客户端就会通过新建立的 TCP 管道来发送 HTTP 请求。数据到达时,Web 服务器会从 TCP 连接中读取请求报文,并对请求进行处理。因特网传输请求报文,以及服务器处理请求报文都需要时间。
(4) 然后,Web 服务器会回送 HTTP 响应,这也需要花费时间。
这些 TCP 网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。TCP 协议的技术复杂性也会对时延产生巨大的影响。
4.2.2 性能聚焦区域
。TCP 连接建立握手;
• TCP 慢启动拥塞控制;
• 数据聚集的 Nagle 算法;
• 用于捎带确认的 TCP 延迟确认算法;
• TIME_WAIT 时延和端口耗尽。 
4.2.3 TCP连接的握手时延
建立一条新的 TCP 连接时,甚至是在发送任意数据之前,TCP 软件之间会交换一系列的 IP 分组,对连接的有关参数进行沟通

TCP 连接握手需要经过以下几个步骤。
(1) 请求新的 TCP 连接时,客户端要向服务器发送一个小的 TCP 分组(通常是 40 ~60 个字节)。这个分组中设置了一个特殊的 SYN 标记,说明这是一个连接请求。
(2) 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP 分组,这个分组中的 SYN 和 ACK 标记都被置位,说明连接请求已被接受
(3) 最后,客户端向服务器回送一条确认信息,通知它连接已成功建立。现代的 TCP 栈都允许客户端在这个确认分组中发送数据。
4.2.4 延迟确认
由于因特网自身无法确保可靠的分组传输(因特网路由器超负荷的话,可以随意丢弃分组),所以 TCP 实现了自己的确认机制来确保数据的成功传输。
延迟确认算法会在一个特定的窗口时间(通常是 100 ~ 200 毫秒)内将输出确认存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段内没有输出数据分组,就将确认信息放在单独的分组中传送。
4.2.5 TCP慢启动
TCP 数据传输的性能还取决于 TCP 连接的使用期(age)。TCP 连接会随着时间进行自我“调谐”,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为 TCP 慢启动(slow start),用于防止因特网的突然过载和拥塞。 
4.2.6 TIME_WAIT累积与端口耗尽
TIME_WAIT 端口耗尽是很严重的性能问题,会影响到性能基准,但在现实中相对较少出现。当某个 TCP 端点关闭 TCP 连接时,会在内存中维护一个小的控制块,用来记录最
近所关闭连接的 IP 地址和端口号。这类信息只会维持一小段时间,通常是所估计的最大分段使用期的两倍(称为 2MSL,通常为 2 分钟 8)左右,以确保在这段时间内不会创建具有相同地址和端口号的新连接。实际上,这个算法可以防止在两分钟内创建、关闭并重新创建两个具有相同 IP 地址和端口号的连接。
客户端每次连接到服务器上去时,都会获得一个新的源端口,以实现连接的唯一性。但由于可用源端口的数量有限(比如,60 000 个),而且在 2MSL 秒(比如,120
秒)内连接是无法重用的,连接率就被限制在了 60 000/120=500 次 / 秒
如果再不断进行优化,并且服务器的连接率不高于 500 次 / 秒,就可确保不会遇到 TIME_WAIT 端口耗尽问题。要修正这个问题,可以增加客户端负载生成机器的数量,或
者确保客户端和服务器在循环使用几个虚拟 IP 地址以增加更多的连接组合。即使没有遇到端口耗尽问题,也要特别小心有大量连接处于打开状态的情况,或为处于等待状态的连接分配了大量控制块的情况。在有大量打开连接或控制块的情况下,有些操作系统的速度会严重减缓。 
4.3 HTTP连接的处理 
4.3.1 常被误解的Connection首部
HTTP 允许在客户端和最终的源端服务器之间存在一串 HTTP 中间实体(代理、高速缓存等)
Connection 首部可以承载 3 种不同类型的标签,因此有时会很令人费解:
• HTTP 首部字段名,列出了只与此连接有关的首部;
• 任意标签值,用于描述此连接的非标准选项;
• 值 close,说明操作完成之后需关闭这条持久连接。

HTTP 应用程序收到一条带有 Connection 首部的报文时,接收端会解析发送端请求的所有选项,并将其应用。然后会在将此报文转发给下一跳地址之前,删除Connection 首部以及 Connection 中列出的所有首部。而且,可能还会有少量没有作为 Connection 首部值列出,但一定不能被代理转发的逐跳首部。其中包括Prxoy-Authenticate、Proxy-Connection、Transfer-Encoding 和 Upgrade。
4.3.2 串行事务处理时延
如果只对连接进行简单的管理,TCP 的性能时延可能会叠加起来。

串行加载的另一个缺点是,有些浏览器在对象加载完毕之前无法获知对象的尺寸,而且它们可能需要尺寸信息来决定将对象放在屏幕的什么位置上,所以在加载了足够多的对象之前,无法在屏幕上显示任何内容。
4.4 并行连接 
HTTP 允许客户端打开多条连接,并行地执行多个 HTTP 事务

4.4.1 并行连接可能会提高页面的加载速度

4.5 持久连接
Web 客户端经常会打开到同一个站点的连接。比如,一个 Web 页面上的大部分内嵌图片通常都来自同一个 Web 站点,而且相当一部分指向其他对象的超链通常都指向同一个站点。因此,初始化了对某服务器 HTTP 请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如,获取在线图片)。这种性质被称为站点局部性(site locality)。
4.5.1 持久以及并行连接
并行连接可以提高复合页面的传输速度。但并行连接也有一些缺点。
• 每个事务都会打开 / 关闭一条新的连接,会耗费时间和带宽。
• 由于 TCP 慢启动特性的存在,每条新连接的性能都会有所降低。
• 可打开的并行连接数量实际上是有限的
持久连接有一些比并行连接更好的地方。持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而且减少了打开连接的潜在数量。但是,管理持久连接时要特别小心,不然就会累积出大量的空闲连接,耗费本地以及远程客户端和服务器上的资源。
4.5.2 Keep-Alive操作
keep-alive 已经不再使用了,而且在当前的 HTTP/1.1 规范中也没有对它的说明了。但浏览器和服务器对 keep-alive 握手的使用仍然相当广泛,因此,HTTP 的实现者应该做好与之进行交互操作的准备。
如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部如果响应中没有 Connection: Keep-Alive 首部,客户端就认为服务器不支持 keep-alive,会在发回响应报文之后关闭连接。

4.5.3 Keep-Alive选项
Keep-Alive 通用首部中指定的、由逗号分隔的选项来调节 keep-alive 的行为。 
• 参数 timeout 是在 Keep-Alive 响应首部发送的。它估计了服务器希望将连接保持在活跃状态的时间。这并不是一个承诺值。
• 参数 max 是在 Keep-Alive 响应首部发送的。它估计了服务器还希望为多少个事务保持此连接的活跃状态。这并不是一个承诺值。
• Keep-Alive 首部还可支持任意未经处理的属性,这些属性主要用于诊断和调试。语法为 name [=value]
Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
4.5.4 Keep-Alive和哑代理
Web 客 户 端 的Connection: Keep-Alive 首部应该只会对这条离开客户端的 TCP 链路产生影响。这就是将其称作“连接”首部的原因。如果客户端正在与一台 Web 服务器对话,客户端可以发送一个 Connection: Keep-Alive 首部来告知服务器它希望保持连接的活跃状态。如果服务器支持 keep-alive,就回送一个 Connection: Keep-Alive首部,否则就不回送。
1. Connection首部和盲中继
问题出在代理上——尤其是那些不理解 Connection 首部,而且不知道在沿着转发链路将其发送出去之前,应该将该首部删除的代理。很多老的或简单的代理都是盲中继(blind relay),它们只是将字节从一个连接转发到另一个连接中去,不对Connection 首部进行特殊的处理。
(1) Web 客户端向代理发送了一条报文,其中包含了 Connection:Keep-Alive 首部,如果可能的话请求建立一条 keep-alive 连接。客户端等待响应,以确定对方是否认可它对 keep-alive 信道的请求。
(2) 哑代理收到了这条 HTTP 请求,但它并不理解 Connection 首部(只是将其作为一个扩展首部对待)。代理不知道 keep-alive 是什么意思,因此只是沿着转发链路将报文一字不漏地发送给服务器。但 Connection 首部是个逐跳首部,只适用于单条传输链路,不应该沿着传输链路向下传输。接下来,就要发生一些很糟糕的事情了
(3) 经过中继的 HTTP 请求抵达了 Web 服务器。当 Web 服务器收到经过代理转发的 Connection: Keep-Alive 首部时,会误以为代理(对服务器来说,这个代理看起来就和所有其他客户端一样)希望进行 keep-alive 对话!对Web 服务器来说这没什么问题——它同意进行 keep-alive 对话,回送了一个 Connection: Keep-Alive 响应首部。所以,此时 Web 服务器认为它在与代理进行 keep-alive 对话,会遵循 keep-alive 的规则。但代理却对 keep-alive 一无所知。不妙。
(4) 哑代理将 Web 服务器的响应报文回送给客户端,并将来自 Web服务器的 Connection: Keep-Alive 首部一起传送过去。客户端看到这个首部,就会认为代理同意进行 keep-alive 对话。所以,此时客户端和服务器都认为它们在进行 keep-alive 对话,但与它们进行对话的代理却对 keep-alive 一无所知。
(5) 由于代理对 keep-alive 一无所知,所以会将收到的所有数据都回送给客户端,然后等待源端服务器关闭连接。但源端服务器会认为代理已经显式地请求它将连接保持在打开状态了,所以不会去关闭连接。这样,代理就会挂在那里等待连接的关闭。
(6) 客户端收到了回送的响应报文时,会立即转向下一条请求,在 keep-alive 连接上向代理发送另一条请求(参见图 4-15e)。而代理并不认为同一条连接上会有其他请求到来,请求被忽略,浏览器就在这里转圈,不会有任何进展了。
(7) 这种错误的通信方式会使浏览器一直处于挂起状态,直到客户端或服务器将连接超时,并将其关闭为止。
2. 代理和逐跳首部
为避免此类代理通信问题的发生,现代的代理都绝不能转发 Connection 首部和所有名字出现在 Connection 值中的首部。因此,如果一个代理收到了一个Connection: Keep-Alive 首部,是不应该转发 Connection 首部,或所有名为Keep-Alive 的首部的
还有几个不能作为 Connection 首部值列出,也不能被代理转发或作为缓存响应使用的首部。其中包括 Proxy-Authenticate、Proxy-Connection、Transfer-Encoding 和 Upgrade。
4.5.6 插入Proxy-Connection
哑代理盲目地转发 Connection: Keep-Alive 之类的逐跳首部惹出了麻烦。逐跳首部只与一条特定的连接有关,不能被转发。当下游服务器误将转发来的首部作为来自代理自身的请求解释,用它来控制自己的连接时,就会引发问题
 

 

 

 

 4.5.7 HTTP/1.1持久连接

 HTTP/1.1 逐渐停止了对keep-alive 连接的支持,用一种名为持久连接(persistentconnection)的改进型设计取代了它。持久连接的目的与keep-alive 连接的目的相同,但工作机制更优一些。
 4.6 管道化连接
 HTTP/1.1 允许在持久连接上可选地使用请求管道。这是相对于keep-alive 连接的又一性能优化。在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。

对管道化连接有几条限制。
• 如果 HTTP 客户端无法确认连接是持久的,就不应该使用管道。
• 必须按照与请求相同的顺序回送 HTTP 响应。HTTP 报文中没有序列号标签,因此如果收到的响应失序了,就没办法将其与请求匹配起来了。
• HTTP 客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完成的管道化请求。如果客户端打开了一条持久连接,并立即发出了10 条请求,服务器可能在只处理了,比方说,5 条请求之后关闭连接。剩下的5 条请求会失败,客户端必须能够应对这些过早关闭连接的情况,重新发出这些请求。
• HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(比如 POST)。总之,出错的时候,管道化方式会阻碍客户端了解服务器执行的是一系列管道化请求中的哪一些。由于无法安全地重试POST 这样的非幂等请求,所以出错时,就存在某些方法永远不会被执行的风险。

 

 

 

 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2020-03-27 21:29  星火撩原  阅读(184)  评论(0编辑  收藏  举报