HTTP/1.1与HTTP/2:有什么区别?

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Society of Women Engineers 接受捐赠。

介绍

超文本传输协议(HTTP)是一种应用协议,自 1989 年发明以来一直是万维网上通信的事实标准。 从 1997 年 HTTP/1.1 发布到最近,该协议几乎没有修改过。 但在 2015 年,一个名为 HTTP/2 的重新构想版本开始使用,它提供了多种降低延迟的方法,尤其是在处理移动平台和服务器密集型图形和视频时。 HTTP/2 此后变得越来越流行,一些估计表明世界上大约三分之一的网站都支持它。 在这种不断变化的环境中,Web 开发人员可以从了解 HTTP/1.1 和 HTTP/2 之间的技术差异中受益,从而使他们能够就不断发展的最佳实践做出明智而有效的决策。

阅读本文后,您将了解 HTTP/1.1 和 HTTP/2 之间的主要区别,重点介绍 HTTP/2 为实现更高效的 Web 协议而采用的技术变化。

背景

为了了解 HTTP/2 对 HTTP/1.1 所做的具体更改,让我们首先从高层次上看一下它们的历史发展和基本工作原理。

HTTP/1.1

HTTP 由 Timothy Berners-Lee 于 1989 年开发,作为万维网的通信标准,是一种顶级应用程序协议,用于在客户端计算机和本地或远程 Web 服务器之间交换信息。 在此过程中,客户端通过调用 GETPOST方法 向服务器发送基于文本的请求。 作为响应,服务器将诸如 HTML 页面之类的资源发送回客户端。

例如,假设您正在访问域 www.example.com 的网站。 当您导航到此 URL 时,您计算机上的 Web 浏览器会以基于文本的消息的形式发送 HTTP 请求,类似于此处显示的消息:

GET /index.html HTTP/1.1
Host: www.example.com

此请求使用 GET 方法,该方法从 Host: 之后列出的主机服务器请求数据。 作为对这个请求的响应,example.com Web 服务器向请求的客户端返回一个 HTML 页面,以及 HTML 中需要的任何图像、样式表或其他资源。 请注意,并非所有资源都在第一次数据调用中返回给客户端。 请求和响应将在服务器和客户端之间来回传递,直到 Web 浏览器接收到在您的屏幕上呈现 HTML 页面内容所需的所有资源。

您可以将这种请求和响应的交换视为 Internet 协议栈的单个 应用层 ,位于 传输层 之上(通常使用传输控制协议或 TCP ) 和 网络层 (使用 Internet 协议或 IP):

关于这个堆栈的较低级别有很多要讨论的内容,但是为了获得对 HTTP/2 的高级理解,您只需要了解这个抽象层模型以及 HTTP 在其中的位置。

了解了 HTTP/1.1 的基本概述后,我们现在可以继续讲述 HTTP/2 的早期发展。

HTTP/2

HTTP/2 最初是 SPDY 协议,主要由 Google 开发,旨在通过使用压缩、多路复用和优先级等技术来减少网页加载延迟。 当 IETF(互联网工程任务组) 的超文本传输协议工作组 httpbis 将标准放在一起时,该协议充当了 HTTP/2 的模板,最终于 2015 年 5 月发布了 HTTP/2。 从一开始,许多浏览器就支持这种标准化工作,包括 Chrome、Opera、Internet Explorer 和 Safari。 部分由于这种浏览器支持,自 2015 年以来该协议的采用率很高,新站点的采用率尤其高。

从技术角度来看,区分 HTTP/1.1 和 HTTP/2 的最显着特征之一是二进制帧层,可以将其视为 Internet 协议栈中应用层的一部分。 与将所有请求和响应保持为纯文本格式的 HTTP/1.1 不同,HTTP/2 使用二进制框架层以二进制格式封装所有消息,同时仍保持 HTTP 语义,例如动词、方法和标头。 应用程序级别的 API 仍会以传统的 HTTP 格式创建消息,但底层会将这些消息转换为二进制文件。 这确保了在 HTTP/2 之前创建的 Web 应用程序在与新协议交互时可以继续正常运行。

将消息转换为二进制允许 HTTP/2 尝试 HTTP/1.1 中不可用的新数据传递方法,这种对比是两种协议之间实际差异的根源。 下一节将介绍 HTTP/1.1 的交付模型,然后介绍 HTTP/2 使哪些新模型成为可能。

交付模式

如上一节所述,HTTP/1.1 和 HTTP/2 共享语义,使用熟悉的方法,如 [X288X ] 和 POST。 但是,虽然 HTTP/1.1 以纯文本消息的形式传输这些信息,但 HTTP/2 将这些信息编码为二进制,从而允许显着不同的交付模型可能性。 在本节中,我们将首先简要介绍 HTTP/1.1 如何尝试通过其交付模型优化效率以及由此产生的问题,然后介绍 HTTP/2 二进制框架层的优势以及如何确定优先级要求。

HTTP/1.1 — 流水线和行头阻塞

客户端在 HTTP GET 请求上收到的第一个响应通常不是完全呈现的页面。 相反,它包含指向所请求页面所需的其他资源的链接。 客户端仅在下载页面后才发现页面的完整呈现需要来自服务器的这些额外资源。 因此,客户端将不得不发出额外的请求来检索这些资源。 在 HTTP/1.0 中,客户端必须为每个新请求中断并重新建立 TCP 连接,这在时间和资源方面都是一项代价高昂的事情。

HTTP/1.1 通过引入持久连接和流水线来解决这个问题。 对于持久连接,HTTP/1.1 假定 TCP 连接应该保持打开状态,除非直接被告知关闭。 这允许客户端沿同一连接发送多个请求,而无需等待每个请求的响应,从而大大提高了 HTTP/1.1 相对于 HTTP/1.0 的性能。

不幸的是,这种优化策略存在一个自然瓶颈。 由于多个数据包在到达同一个目的地时无法相互传递,因此在某些情况下,队列头部的请求无法检索其所需资源,将阻塞其后面的所有请求。 这被称为 线头 (HOL) 阻塞 ,是 HTTP/1.1 中优化连接效率的一个重要问题。 添加单独的并行 TCP 连接可以缓解此问题,但客户端和服务器之间可能的并发 TCP 连接数量存在限制,并且每个新连接都需要大量资源。

这些问题是 HTTP/2 开发人员最关心的问题,他们建议使用前面提到的二进制框架层来解决这些问题,您将在下一节中详细了解这个主题。

HTTP/2 — 二进制帧层的优点

在 HTTP/2 中,二进制成帧层对请求/响应进行编码,并将它们分割成更小的信息包,大大增加了数据传输的灵活性。

让我们仔细看看它是如何工作的。 与 HTTP/1.1 不同,它必须使用多个 TCP 连接来减少 HOL 阻塞的影响,HTTP/2 在两台机器之间建立一个单一的连接对象。 在此连接中,有多个 数据。 每个流由熟悉的请求/响应格式的多条消息组成。 最后,这些消息中的每一个都拆分为更小的单元,称为 frames

在最细粒度的级别上,通信通道由一堆二进制编码的帧组成,每个帧都标记到一个特定的流。 识别标签允许连接在传输期间交错这些帧,并在另一端重新组装它们。 交错的请求和响应可以并行运行而不会阻塞它们背后的消息,这个过程称为 多路复用 。 多路复用通过确保没有消息必须等待另一个消息完成来解决 HTTP/1.1 中的行头阻塞问题。 这也意味着服务器和客户端可以发送并发请求和响应,从而实现更好的控制和更有效的连接管理。

由于多路复用允许客户端并行构建多个流,因此这些流只需要使用单个 TCP 连接。 通过减少整个网络的内存和处理占用空间,每个源拥有一个持久连接改进了 HTTP/1.1。 这可以提高网络和带宽利用率,从而降低总体运营成本。

单个 TCP 连接还提高了 HTTPS 协议的性能,因为客户端和服务器可以为多个请求/响应重用相同的安全会话。 在 HTTPS 中,在 TLS 或 SSL 握手期间,双方同意在整个会话期间使用单个密钥。 如果连接中断,则会启动一个新会话,需要新生成的密钥进行进一步通信。 因此,维护单个连接可以大大减少 HTTPS 性能所需的资源。 请注意,尽管 HTTP/2 规范并未强制使用 TLS 层,但许多主流浏览器仅支持带有 HTTPS 的 HTTP/2。

尽管二进制帧层固有的多路复用解决了 HTTP/1.1 的某些问题,但等待同一资源的多个流仍然会导致性能问题。 HTTP/2 的设计考虑到了这一点,然而,通过使用流优先级,我们将在下一节讨论这个主题。

HTTP/2 — 流优先级

流优先级不仅解决了请求竞争相同资源的可能问题,还允许开发人员自定义请求的相对权重,以更好地优化应用程序性能。 在本节中,我们将分解此优先级的过程,以便更好地了解如何利用 HTTP/2 的此功能。

如您所知,二进制框架层将消息组织成并行的数据流。 当客户端向服务器发送并发请求时,它可以通过为每个流分配 1 到 256 之间的权重来确定其请求的响应的优先级。 数字越大表示优先级越高。 除此之外,客户端还通过指定它所依赖的流的 ID 来声明每个流对另一个流的依赖。 如果省略父标识符,则认为该流依赖于根流。 如下图所示:

在插图中,通道包含六个流,每个流都有一个唯一的 ID 并与特定的权重相关联。 Stream 1 没有与之关联的父 ID,默认情况下与根节点关联。 所有其他流都标记了一些父 ID。 每个流的资源分配将基于它们所拥有的权重和它们所需的依赖关系。 例如,流 5 和 6,在图中被分配了相同的权重和相同的父流,将具有相同的资源分配优先级。

服务器使用此信息创建一个依赖关系树,它允许服务器确定请求检索其数据的顺序。 根据上图中的流,依赖树如下:

在这个依赖树中,流 1 依赖于根流,并且没有从根流派生的其他流,因此所有可用资源将在其他流之前分配给流 1。 由于树指示流 2 依赖于流 1 的完成,因此流 2 将在流 1 任务完成之前不会继续。 现在,让我们看一下流 3 和 4。 这两个流都依赖于流 2。 与流 1 的情况一样,流 2 将在流 3 和 4 之前获得所有可用资源。 流 2 完成任务后,流 3 和流 4 将获得资源; 正如它们的权重所示,它们以 2:4 的比例进行拆分,从而为流 4 提供更高的资源块。 最后,当流 3 结束时,流 5 和流 6 将获得相等部分的可用资源。 这可能发生在流 4 完成其任务之前,即使流 4 接收到更多的资源块; 一旦上层的依赖流完成,就允许下层的流开始。

作为应用程序开发人员,您可以根据需要在请求中设置权重。 例如,您可以在网页上提供缩略图后,为加载高分辨率图像分配较低的优先级。 通过提供这种权重分配功能,HTTP/2 使开发人员能够更好地控制网页呈现。 该协议还允许客户端在运行时更改依赖关系并重新分配权重以响应用户交互。 然而,重要的是要注意,如果某个流被阻止访问特定资源,服务器可能会自行更改分配的优先级。

缓冲区溢出

在两台机器之间的任何 TCP 连接中,客户端和服务器都有一定数量的缓冲区空间可用于保存尚未处理的传入请求。 除了下游和上游连接的不均匀速度之外,这些缓冲区还提供了处理大量或特别大的请求的灵活性。

然而,在某些情况下,缓冲区是不够的。 例如,由于缓冲区大小有限或带宽较低,服务器可能会以客户端应用程序无法应对的速度推送大量数据。 同样,当客户端将巨大的图像或视频上传到服务器时,服务器缓冲区可能会溢出,导致一些额外的数据包丢失。

为了避免缓冲区溢出,流控制机制必须防止发送方用数据压倒接收方。 本节将概述 HTTP/1.1 和 HTTP/2 如何根据其不同的交付模型使用该机制的不同版本来处理流量控制。

HTTP/1.1

在 HTTP/1.1 中,流量控制依赖于底层的 TCP 连接。 当此连接启动时,客户端和服务器都使用它们的系统默认设置来建立它们的缓冲区大小。 如果接收方的缓冲区被部分数据填满,它将告诉发送方其接收窗口,即缓冲区中剩余的可用空间量。 此接收窗口在称为 ACK 数据包 的信号中进行通告,该信号是接收器发送的数据包,以确认它收到了打开信号。 如果这个通告的接收窗口大小为零,则发送方将不再发送数据,直到客户端清除其内部缓冲区并请求恢复数据传输。 这里需要注意的是,使用基于底层 TCP 连接的接收窗口只能在连接的任一端实现流量控制。

因为 HTTP/1.1 依赖传输层来避免缓冲区溢出,所以每个新的 TCP 连接都需要单独的流量控制机制。 然而,HTTP/2 在单个 TCP 连接中多路复用流,并且必须以不同的方式实现流控制。

HTTP/2

HTTP/2 在单个 TCP 连接中多路复用数据流。 因此,TCP 连接级别的接收窗口不足以规范单个流的传递。 HTTP/2 通过允许客户端和服务器实现自己的流控制,而不是依赖于传输层来解决这个问题。 应用层传达可用的缓冲区空间,允许客户端和服务器在多路复用流的级别上设置接收窗口。 在通过 WINDOW_UPDATE 帧进行初始连接后,可以修改或维护这种精细的流量控制。

由于该方法在应用层层面控制数据流,因此流控机制不必等到信号到达最终目的地后才调整接收窗口。 中间节点可以使用流控制设置信息来确定它们自己的资源分配并进行相应的修改。 这样,每个中间服务器都可以实现自己的自定义资源策略,从而提高连接效率。

在创建适当的资源策略时,流控制的这种灵活性可能是有利的。 例如,客户端可以获取图像的第一次扫描,将其显示给用户,并允许用户在获取更多关键资源的同时预览它。 一旦客户端获取到这些关键资源,浏览器将继续检索图像的剩余部分。 因此,将流控制的实施推迟到客户端和服务器可以提高 Web 应用程序的感知性能。

在前面部分提到的流控制和流优先级方面,HTTP/2 提供了更详细的控制级别,从而为更大的优化提供了可能性。 下一节将解释另一种协议独有的方法,它可以以类似的方式增强连接:使用 服务器推送 预测资源请求。

预测资源请求

在典型的 Web 应用程序中,客户端将发送 GET 请求并接收 HTML 格式的页面,通常是网站的索引页面。 在检查索引页面内容时,客户端可能会发现它需要获取额外的资源,例如 CSS 和 JavaScript 文件,才能完全呈现页面。 客户端仅在收到其初始 GET 请求的响应后才确定它需要这些额外资源,因此必须发出额外请求以获取这些资源并完成页面组合。 这些额外的请求最终会增加连接加载时间。

然而,这个问题有一些解决方案:由于服务器预先知道客户端需要额外的文件,服务器可以通过在请求之前将这些资源发送给客户端来节省客户端时间。 HTTP/1.1 和 HTTP/2 有不同的策略来实现这一点,每一个都将在下一节中描述。

HTTP/1.1 — 资源内联

在 HTTP/1.1 中,如果开发人员事先知道客户端机器需要哪些额外资源来呈现页面,他们可以使用称为 资源内联 的技术将所需资源直接包含在 HTML 文档中服务器发送以响应初始 GET 请求。 例如,如果客户端需要特定的 CSS 文件来呈现页面,则内联该 CSS 文件将在客户端请求之前为客户端提供所需的资源,从而减少客户端必须发送的请求总数。

但是资源内联存在一些问题。 将资源包含在 HTML 文档中对于较小的基于文本的资源是一种可行的解决方案,但非文本格式的较大文件会大大增加 HTML 文档的大小,最终会降低连接速度并抵消原有的优势从使用这种技术。 此外,由于内联资源不再与 HTML 文档分离,因此客户端没有机制拒绝它已经拥有的资源,或将资源放置在其缓存中。 如果多个页面需要该资源,则每个新的 HTML 文档都将在其代码中内联相同的资源,从而导致 HTML 文档更大,加载时间也比开始时简单地缓存资源时更长。

因此,资源内联的一个主要缺点是客户端无法分离资源和文档。 需要更精细的控制来优化连接,这是 HTTP/2 寻求通过服务器推送来满足的需求。

HTTP/2 — 服务器推送

由于 HTTP/2 允许对客户端的初始 GET 请求进行多个并发响应,因此服务器可以将资源与请求的 HTML 页面一起发送给客户端,在客户端请求之前提供资源。 这个过程称为服务器推送。 通过这种方式,一个 HTTP/2 连接可以实现资源内联的相同目标,同时保持推送资源和文档之间的分离。 这意味着客户端可以决定缓存或拒绝与主 HTML 文档分开的推送资源,从而解决资源内联的主要缺点。

在 HTTP/2 中,当服务器发送 PUSH_PROMISE 帧以通知客户端它将推送资源时,此过程开始。 此帧仅包含消息的标头,并允许客户端提前知道服务器将推送哪个资源。 如果它已经缓存了资源,客户端可以通过发送 RST_STREAM 帧作为响应来拒绝推送。 PUSH_PROMISE 框架还避免了客户端向服务器发送重复请求,因为它知道服务器将推送哪些资源。

这里需要注意的是,服务器推送的重点是客户端控制。 如果客户端需要调整服务器推送的优先级,甚至禁用它,它可以随时发送一个 SETTINGS 帧来修改这个 HTTP/2 特性。

尽管此功能有很大的潜力,但服务器推送并不总是优化您的 Web 应用程序的答案。 例如,一些 Web 浏览器不能总是取消推送的请求,即使客户端已经缓存了资源。 如果客户端错误地允许服务器发送重复资源,服务器推送可能会不必要地耗尽连接。 最后,应由开发人员自行决定是否使用服务器推送。 有关如何战略性地使用服务器推送和优化 Web 应用程序的更多信息,请查看 Google 开发的 PRPL 模式。 要了解有关服务器推送可能出现的问题的更多信息,请参阅 Jake Archibald 的博文 HTTP/2 推送比我想象的要困难

压缩

优化 Web 应用程序的一种常用方法是使用压缩算法来减少在客户端和服务器之间传输的 HTTP 消息的大小。 HTTP/1.1 和 HTTP/2 都使用了这种策略,但是前者存在实现问题,禁止压缩整个消息。 以下部分将讨论为什么会出现这种情况,以及 HTTP/2 如何提供解决方案。

HTTP/1.1

gzip 等程序长期以来一直用于压缩 HTTP 消息中发送的数据,尤其是用于减小 CSS 和 JavaScript 文件的大小。 但是,消息的标题部分始终以纯文本形式发送。 尽管每个标头都很小,但随着请求的增加,这种未压缩数据的负担在连接上的负担越来越重,特别是对需要许多不同资源的复杂、API 繁重的 Web 应用程序,因此需要许多不同的资源请求。 此外,cookie 的使用有时会使标头变得更大,从而增加了对某种压缩的需求。

为了解决这个瓶颈,HTTP/2 使用 HPACK 压缩来缩小标头的大小,下一节将进一步讨论这个主题。

HTTP/2

HTTP/2 中反复出现的主题之一是它能够使用二进制框架层来展示对更精细细节的更好控制。 对于标头压缩也是如此。 HTTP/2 可以将标头从它们的数据中分离出来,从而产生一个标头帧和一个数据帧。 HTTP/2 特定的压缩程序 HPACK 然后可以压缩这个头帧。 该算法可以使用 Huffman 编码对标头元数据进行编码,从而大大减小其大小。 此外,HPACK 可以跟踪先前传送的元数据字段,并根据客户端和服务器之间共享的动态更改的索引进一步压缩它们。 例如,采取以下两个请求:

请求 #1

method:      GET
scheme:     https
host:       example.com
path:       /academy
accept:     /image/jpeg
user-agent: Mozilla/5.0 ...

请求 #2

method:      GET
scheme:     https
host:       example.com
path:       /academy/images
accept:     /image/jpeg
user-agent: Mozilla/5.0 ...

这些请求中的各个字段,例如methodschemehostacceptuser-agent,具有相同的值; 只有 path 字段使用不同的值。 因此,在发送 Request #2 时,客户端可以使用 HPACK 仅发送重建这些公共字段所需的索引值并重新编码 path 字段。 生成的标头帧如下:

请求 #1 的标头帧

method:      GET
scheme:     https
host:       example.com
path:       /academy
accept:     /image/jpeg
user-agent: Mozilla/5.0 ...

请求 #2 的标头帧

path:        /academy/images

使用 HPACK 和其他压缩方法,HTTP/2 提供了另外一项可以减少客户端-服务器延迟的特性。

结论

从这个逐点分析中可以看出,HTTP/2 在许多方面与 HTTP/1.1 不同,其中一些特性提供了更高级别的控制,可用于更好地优化 Web 应用程序性能,而其他特性只是在以前的协议。 既然您已经对两种协议之间的差异有了一个高层次的了解,您可以考虑 HTTP/2 中的多路复用、流优先级、流控制、服务器推送和压缩等因素将如何影响 Web 开发的不断变化的格局.

如果您想查看 HTTP/1.1 和 HTTP/2 之间的性能比较,请查看此 Google 演示 ,它比较了不同延迟的协议。 请注意,当您在计算机上运行测试时,页面加载时间可能会因多种因素而异,例如带宽、测试时可用的客户端和服务器资源等。 如果您想研究更详尽的测试结果,请查看文章 HTTP/2 – A Real-World Performance Test and Analysis。 最后,如果您想探索如何构建现代 Web 应用程序,您可以按照我们的 如何构建现代 Web 应用程序以在 Ubuntu 18.04 上使用 Django 和 React 管理客户信息教程,或设置您的使用我们的 如何在 Ubuntu 20.04 教程上设置支持 HTTP/2 的 Nginx 拥有自己的 HTTP/2 服务器。