#Network

设计和技术目标

早期版本的 HTTP 协议的设计初衷是为了简化实现:HTTP/0.9 是用来引导万维网的一行协议;HTTP/1.0 以信息性标准记录了 HTTP/0.9 的热门扩展;HTTP/1.1 则引入了官方 IETF 标准;请参阅 HTTP 简史 。因此,HTTP/0.9-1.x 可以完全实现其预期用途:HTTP 是互联网上最广泛采用的应用协议之一。

遗憾的是,实现简单是以牺牲应用性能为代价的:HTTP/1.x 客户端需要使用多个连接来实现并发并缩短延迟时间;HTTP/1.x 不会压缩请求和响应标头,从而导致不必要的网络流量;HTTP/1.x 不允许有效的资源优先级排序,从而导致底层 TCP 连接被滥用;等等。

这些限制并不是致命的,但随着 Web 应用的范围、复杂性和在我们日常生活中的重要性不断增长,它们给 Web 开发者和用户造成了越来越大的负担,而这正是 HTTP/2 旨在解决的问题:

HTTP/2 通过引入标头字段压缩并允许在同一连接上交错进行多个并发交换,可以更高效地利用网络资源并减少对延迟的感知。具体而言,它允许在同一连接上交错请求和响应消息,并对 HTTP 标头字段使用高效的编码。它还允许对请求进行优先级排序,让更重要的请求更快速地完成,从而进一步提升性能。

生成的协议对网络更友好,因为与 HTTP/1.x 相比,可以使用的 TCP 连接更少。这意味着与其他流的竞争更少,并且连接的持续时间更长,进而可以更好地利用可用网络容量。最后,HTTP/2 还可以通过使用二进制消息分帧来更高效地处理消息。(超文本传输协议版本 2,草稿 17)

请务必注意,HTTP/2 是对以前的 HTTP 标准的扩展,而不是取代。HTTP 的应用语义相同,提供的功能或核心概念(例如 HTTP 方法、状态代码、URI 和标头字段)没有发生任何变化。这些更改显然不在 HTTP/2 工作的范围之内。也就是说,虽然高层级 API 保持不变,但了解低层级更改如何解决了先前协议的性能限制仍非常重要。我们来简单了解一下二进制分帧层及其功能。

二进制分帧层

HTTP/2 所有性能增强的核心是新的二进制分帧层,它规定了 HTTP 消息的封装方式,并在客户端和服务器之间传输。

HTTP/2 Frame

“层”是指一种设计选择,用于在套接字接口与提供给应用的更高级别 HTTP API 之间引入一种经过优化的新编码机制:HTTP 语义(例如动词、方法和标头)不受影响,但其在传输过程中的编码方式有所不同。与以换行符分隔的 HTTP/1.x 协议明文形式不同,所有 HTTP/2 通信都会拆分为较小的消息和帧,其中的每个消息和帧都采用二进制格式进行编码。

因此,客户端和服务器都必须使用新的二进制编码机制来相互理解:HTTP/1.x 客户端无法理解仅支持 HTTP/2 的服务器,反之亦然。幸运的是,我们的应用仍可轻松了解所有这些变化,因为客户端和服务器会代表我们执行所有必要的取景工作。

Binary protocol versus text protocol isn’t really about how binary blobs are encoded. The difference is really whether the protocol is oriented around data structures or around text strings. Let me give an example: HTTP. HTTP is a text protocol, even though when it sends a jpeg image, it just sends the raw bytes, not a text encoding of them.

信息流、消息和帧

HTTP/2 Frame

新的二进制分帧机制改变了客户端与服务器之间的数据交换方式。为了描述此过程,我们需要先熟悉 HTTP/2 术语:

  • 数据流:已建立的连接内的双向字节流,可承载一条或多条消息。
  • 消息:映射到逻辑请求或响应消息的完整帧序列。
  • 帧:HTTP/2 中的最小通信单元,每个单元包含一个帧标头,至少标识该帧所属的数据流。

这些术语之间的关系可总结如下:

  • 所有通信都在一个 TCP 连接上完成,该连接可以承载任意数量的双向数据流。
  • 每个数据流都有一个唯一标识符和可选的优先级信息,用于承载双向消息。
  • 每条消息都是一条逻辑 HTTP 消息(例如请求或响应),包含一个或多个帧。
  • 帧是最小的通信单位,承载着特定类型的数据,例如,HTTP 标头、邮件载荷等。来自不同数据流的帧可以交错,然后通过每个帧标头中的嵌入式数据流标识符重新组装。

也就是说,HTTP/2 的每个消息都由一个或多个帧组成,这些帧可以交错发送,然后再在接收端重新组装。帧的顺序并不重要,因为每个帧都包含其相对位置的信息。

请求和响应多路复用

使用 HTTP/1.x 时,如果客户端想要发出多个并行请求以提高性能,则必须使用多个 TCP 连接(请参阅使用多个 TCP 连接)。

注意这里是并行!!
客户端往往会为每个请求创建一个新的连接,以便并行发送请求,达到性能最大化。然而,这种方法会导致一些问题。

这是 HTTP/1.x 传送模型的直接结果,该行为可确保每个连接一次只传送一个响应(响应排队)。更糟糕的是,这也会导致队首阻塞,以及底层 TCP 连接的效率低下。

HTTP/2 中新的二进制分帧层消除了这些限制并实现了完整的请求和响应多路复用,方法是允许客户端和服务器将 HTTP 消息分解为独立的帧,交错发送,然后在另一端重新组装这些帧。

该快照捕捉了同一连接内传输的多个数据流。客户端正在向服务器传输 DATA 帧(流 5),而服务器正在将交错的帧序列发送到客户端,以便流 1 和流 3。因此,正在传输三个并行流。

能够将 HTTP 消息分解为独立的帧,交错这些帧,然后在另一端重新组装这些帧,是 HTTP/2 最重要的增强功能。事实上,它在所有 Web 技术的整个堆栈中带来了众多性能优势的连锁效应,使我们能够:

  • 并行交错地发送多个请求,请求之间互不影响。
  • 并行交错地发送多个响应,响应之间互不影响。
  • 使用一个连接并行发送多个请求和响应。
  • 移除了不必要的 HTTP/1.x 权宜解决方法(请参阅针对 HTTP/1.x 进行优化,例如串联文件、image sprites 和网域分片)。
  • 消除不必要的延迟并提高可用网络容量的利用率,从而缩短网页加载时间。
  • 等等…

HTTP/2 中新的二进制分帧层解决了 HTTP/1.x 中存在的队首阻塞问题,并且消除了并行处理及传送请求和响应所需的多个连接。因此,我们的应用部署速度更快、操作更简单、部署成本更低。

数据流优先级

一旦 HTTP 消息可以拆分为多个单独的帧,并且我们允许对来自多个数据流的帧进行多路复用,那么客户端和服务器交错和传送这些帧的顺序就成为关键的性能考虑因素。为了方便起见,HTTP/2 标准允许每个数据流具有关联的权重和依赖关系:

  • 可以为每个数据流分配一个介于 1 到 256 之间的整数。
  • 每个数据流都可以显式依赖于另一个数据流。

通过结合使用数据流依赖关系和权重,客户端可以构建和传递“优先级树”,表明其希望如何接收响应。反过来,服务器可以使用这些信息通过控制 CPU、内存和其他资源的分配来确定流处理的优先级,并在响应数据可用后分配带宽,以确保将高优先级响应以最优方式传递给客户端。

HTTP/2 Frame

每个源一个连接

有了新的二进制分帧机制,HTTP/2 不再需要多个 TCP 连接来并行复用数据流;每个数据流都会拆分为许多帧,这些帧可以交错并设定优先级。因此,所有 HTTP/2 连接都是永久性的,并且每个来源只需要一个连接,从而提供大量性能优势。

SPDY 和 HTTP/2 的杀手级功能是,在单个拥塞受控的通道上任意进行多路复用。它的重要性和良好运作机制让我惊叹不已我喜欢的一个重要指标是创建的连接所占的比例,这些连接仅承载单个 HTTP 事务(因此该事务承担所有开销)。对于 HTTP/1,我们 74% 的活动连接只承载一个事务 - 持久性连接没有我们所有人想要的那样帮助。但在 HTTP/2 中,这一比例下降至 25%。 这在减少开销方面是一项巨大的成功。(HTTP/2 已在 Firefox 中推出,Patrick McManus)

大多数 HTTP 传输都是短暂的且具有突发性的,而 TCP 则针对长期的批量数据传输进行了优化。通过重复使用同一连接,HTTP/2 既可以更有效地利用每个 TCP 连接,也可以显著降低整体协议开销。此外,使用较少的连接可以减少整个连接路径(即客户端、中间服务器和源服务器)上的内存和处理占用空间。这样可以降低整体运营费用,并提高网络利用率和容量。因此,改用 HTTP/2 不仅可以减少网络延迟,还有助于提高吞吐量并降低运营费用。