HTTP/1.x 中的连接管理
连接管理是 HTTP 中的关键主题:打开和维护连接在很大程度上影响着网站和 Web 应用程序的性能。在 HTTP/1.x 中,有几种模型:短连接、持久连接和HTTP 管道。
HTTP 主要依赖 TCP 作为其传输协议,在客户端和服务器之间提供连接。在 HTTP 的早期,它使用单一模型来处理此类连接。这些连接是短连接:每次需要发送请求时都会创建一个新的连接,并在收到答案后关闭。
这种简单模型在性能上存在一个内在的局限性:打开每个 TCP 连接都是一个资源消耗很大的操作。客户端和服务器之间必须交换多个消息。网络延迟和带宽会影响发送请求时的性能。现代网页需要许多请求(十几个或更多)才能提供所需的信息量,这证明了早期模型的低效性。
HTTP/1.1 中创建了两种更新的模型。持久连接模型保持连接在连续请求之间保持打开状态,从而减少了打开新连接所需的时间。HTTP 管道模型更进一步,通过发送多个连续请求,甚至不等待答案,从而减少了网络中的大部分延迟。
注意:HTTP/2 为连接管理添加了额外的模型。
需要注意的是,HTTP 中的连接管理适用于两个连续节点之间的连接,这是逐跳而不是端到端。客户端与其第一个代理之间的连接所使用的模型可能与代理和目标服务器之间的模型不同(或任何中间代理)。用于定义连接模型的 HTTP 标头,如Connection
和Keep-Alive
,是逐跳标头,其值可以被中间节点更改。
一个相关的主题是 HTTP 连接升级的概念,其中 HTTP/1.1 连接被升级到不同的协议,例如 TLS/1.0、WebSocket,甚至明文 HTTP/2。这种协议升级机制在其他地方有更详细的说明。
短暂连接
HTTP 的原始模型,也是 HTTP/1.0 中的默认模型,是短连接。每个 HTTP 请求都在其自己的连接上完成;这意味着在每个 HTTP 请求之前都会发生 TCP 握手,并且这些握手是串行的。
TCP 握手本身很耗时,但 TCP 连接会适应其负载,在更持久的(或热)连接下变得更高效。短连接不利用 TCP 的此效率特性,并且通过持续传输到新的、冷连接,性能会下降到最佳状态。
这种模型是 HTTP/1.0 中使用的默认模型(如果没有Connection
标头,或者其值为close
)。在 HTTP/1.1 中,只有当Connection
标头发送的值为close
时,才会使用这种模型。
注意:除非处理非常旧的系统,该系统不支持持久连接,否则没有令人信服的理由使用这种模型。
持久连接
短连接有两个主要缺点:建立新连接所需的时间很长,并且底层 TCP 连接的性能只有在该连接使用了一段时间(热连接)后才会变得更好。为了缓解这些问题,在 HTTP/1.1 之前就已经设计了持久连接的概念。或者,这也可以称为保持活动连接。
持久连接是指在一段时间内保持打开状态的连接,可以重复使用于多个请求,从而避免了新的 TCP 握手,并利用了 TCP 的性能增强功能。此连接不会永远保持打开状态:空闲连接会在一段时间后关闭(服务器可以使用Keep-Alive
标头指定连接应保持打开的最短时间)。
持久连接也有一些缺点;即使空闲时,它们也会消耗服务器资源,并且在负载过重的情况下,可以进行DoS 攻击。在这种情况下,使用非持久连接,即在空闲后立即关闭连接,可以提供更好的性能。
HTTP/1.0 连接默认情况下不是持久连接。将Connection
设置为close
以外的任何值,通常为retry-after
,将使其持久化。
在 HTTP/1.1 中,持久性是默认设置,不再需要标头(但通常会添加它作为针对需要回退到 HTTP/1.0 的情况的防御措施)。
HTTP 管道
注意:HTTP 管道在现代浏览器中默认情况下未启用。
- 有问题的代理仍然很常见,它们会导致奇怪且不稳定的行为,Web 开发人员无法轻易预测和诊断。
- 管道在正确实现方面很复杂:正在传输的资源的大小、将使用的有效RTT以及有效带宽,对管道提供的改进具有直接影响。在不知道这些信息的情况下,重要信息可能会被延迟到不重要的信息之后。甚至在页面布局期间,重要性的概念也在不断发展!因此,HTTP 管道在大多数情况下仅带来了微不足道的改进。
- 管道会受到HOL问题的困扰。
由于这些原因,管道已被一种更好的算法——复用所取代,该算法由 HTTP/2 使用。
默认情况下,HTTP请求是按顺序发出的。只有在收到当前请求的响应后才会发出下一个请求。由于受到网络延迟和带宽限制的影响,这可能会导致在下一个请求被服务器看到之前出现明显的延迟。
管道是通过同一个持久连接发送连续请求,而不等待答案的过程。这避免了连接的延迟。理论上,如果将两个 HTTP 请求打包到同一个 TCP 消息中,也可以提高性能。典型的MSS(最大段大小)足够大,可以包含多个简单的请求,尽管 HTTP 请求的大小需求在不断增长。
并非所有类型的 HTTP 请求都可以管道化:只有幂等方法,即GET
、HEAD
、PUT
和DELETE
,可以安全地重放。如果发生故障,可以重复管道内容。
如今,每个符合 HTTP/1.1 标准的代理和服务器都应该支持管道化,尽管在实践中许多代理和服务器都有局限性:这是现代浏览器默认情况下不启用此功能的重要原因。
域名分片
注意:除非你有非常具体的迫切需要,否则不要使用这种已弃用的技术;改为切换到 HTTP/2。在 HTTP/2 中,域分片不再有用:HTTP/2 连接能够很好地处理并行非优先级请求。域分片甚至会损害性能。大多数 HTTP/2 实现使用一种名为连接合并的技术来恢复最终的域分片。
由于 HTTP/1.x 连接是串行化请求的,即使没有任何排序,如果没有足够大的可用带宽,它也不能达到最佳状态。作为解决方案,浏览器对每个域打开多个连接,发送并行请求。默认值曾经是 2 到 3 个连接,但现在已经增加到更常见的 6 个并行连接。如果尝试超过此数量,则存在触发服务器端DoS保护的风险。
如果服务器希望网站或应用程序响应更快,则服务器可以强制打开更多连接。例如,与其将所有资源放在同一个域中,例如www.example.com
,不如将其拆分为多个域,例如www1.example.com
、www2.example.com
、www3.example.com
。这些域中的每一个都解析到同一个服务器,并且 Web 浏览器将对每个域打开 6 个连接(在我们的示例中,连接将增加到 18)。这种技术称为域分片。
结论
改进的连接管理使 HTTP 的性能大幅提升。使用 HTTP/1.1 或 HTTP/1.0,使用持久连接——至少在连接变为空闲之前——可以带来最佳性能。但是,管道化的失败导致设计了更高级的连接管理模型,这些模型已被整合到 HTTP/2 中。