HTTP/3:性能改进(第 2 部分)

已发表: 2022-03-10
快速总结 ↬经过近五年的开发,新的 HTTP/3 协议已接近最终形式。 让我们仔细看看 HTTP/3、拥塞控制、行头阻塞和 0-RTT 连接设置的性能改进。

欢迎回到这个关于新 HTTP/3 协议的系列。 在第 1 部分中,我们研究了为什么我们需要 HTTP/3 和底层 QUIC 协议,以及它们的主要新特性是什么。

在第二部分中,我们将重点介绍 QUIC 和 HTTP/3 为网页加载带来的性能改进。 但是,我们也会对这些新功能在实践中的预期影响持怀疑态度。

正如我们将看到的,QUIC 和 HTTP/3 确实具有巨大的 Web 性能潜力,但主要针对慢速网络的用户。 如果您的普通访问者使用的是快速有线或蜂窝网络,他们可能不会从新协议中获得太多好处。 但是,请注意,即使在通常具有快速上行链路的国家和地区,最慢的 1% 甚至 10% 的受众(所谓的第 99第 90 个百分位数)仍然有可能获得很多收益。 这是因为 HTTP/3 和 QUIC 主要帮助处理当今互联网上可能出现的一些不常见但潜在的高影响问题。

这部分比第一部分更具技术性,尽管它将大部分真正深入的东西卸载到外部资源,重点解释为什么这些东西对普通 Web 开发人员很重要。

  • 第 1 部分:HTTP/3 历史和核心概念
    本文主要针对 HTTP/3 和一般协议的新手,主要讨论基础知识。
  • 第 2 部分:HTTP/3 性能特性
    这一个更深入和技术。 已经了解基础知识的人可以从这里开始。
  • 第 3 部分:实用的 HTTP/3 部署选项
    本系列的第三篇文章解释了自己部署和测试 HTTP/3 所涉及的挑战。 它详细说明了如何以及是否应该更改您的网页和资源。

速度入门

讨论性能和“速度”很快就会变得复杂,因为许多潜在的方面都会导致网页加载“缓慢”。 因为我们这里处理的是网络协议,所以我们将主要看网络方面,其中两个最重要:延迟和带宽。

延迟可以粗略定义为从 A 点(例如客户端)发送数据包到 B 点(服务器)所需的时间。 它在物理上受到光速的限制,或者实际上,信号在电线或户外传播的速度有多快。 这意味着延迟通常取决于 A 和 B 之间的物理距离。

实际上,这意味着典型的延迟在概念上很小,大约在 10 到 200 毫秒之间。 然而,这只是一种方式:对数据包的响应也需要返回。 双向延迟通常称为往返时间 (RTT)

由于拥塞控制等功能(见下文),我们通常需要相当多的往返来加载甚至是单个文件。 因此,即使低于 50 毫秒的低延迟也会导致相当大的延迟。 这是内容交付网络 (CDN) 存在的主要原因之一:它们将服务器放置在物理上更靠近最终用户的位置,以尽可能减少延迟,从而减少延迟。

那么,带宽大致可以说是可以同时发送的数据包的数量。 这有点难以解释,因为它取决于介质的物理特性(例如,使用的无线电波频率)、网络上的用户数量以及互连不同子网的设备(因为它们通常每秒只能处理一定数量的数据包)。

一个常用的比喻是用来输送水的管道。 管道的长度是延迟,而管道的宽度是带宽。 然而,在互联网上,我们通常有一系列连接的管道,其中一些可能比其他管道更宽(导致在最窄的链接处出现所谓的瓶颈)。 因此,A 点和 B 点之间的端到端带宽通常受到最慢子部分的限制。

虽然这篇文章的其余部分不需要对这些概念有一个完美的理解,但有一个通用的高级定义会很好。 有关更多信息,我建议查看 Ilya Grigorik 在他的《高性能浏览器网络》一书中关于延迟和带宽的优秀章节。

拥塞控制

性能的一个方面是关于传输协议如何有效地使用网络的全部(物理)带宽(即粗略地说,每秒可以发送或接收多少数据包)。 这反过来会影响页面资源的下载速度。 有人声称 QUIC 在这方面比 TCP 做得更好,但事实并非如此。

你知道吗?

例如,TCP 连接不仅仅开始以全带宽发送数据,因为这可能最终导致网络过载(或拥塞)。 这是因为,正如我们所说,每个网络链接每秒只能(物理)处理一定数量的数据。 再给它,除了丢弃过多的数据包之外别无选择,导致数据包丢失

如第 1 部分所述,对于像 TCP 这样的可靠协议,从丢包中恢复的唯一方法是重新传输数据的新副本,这需要一个往返。 尤其是在高延迟网络上(例如,超过 50 毫秒的 RTT),数据包丢失会严重影响性能。

另一个问题是我们事先不知道最大带宽是多少。 它通常取决于端到端连接中某处的瓶颈,但我们无法预测或知道它会在哪里。 互联网也没有(还)向端点发送链路容量信号的机制。

此外,即使我们知道可用的物理带宽,也不意味着我们可以自己使用所有这些带宽。 几个用户通常同时在网络上处于活动状态,每个用户都需要公平共享可用带宽。

因此,连接不知道它可以安全或公平地预先使用多少带宽,并且此带宽会随着用户加入、离开和使用网络而改变。 为了解决这个问题,TCP 会通过一种称为拥塞控制的机制不断尝试发现可用带宽。

在连接开始时,它只发送几个数据包(实际上,范围在 10 到 100 个数据包之间,或大约14 到 140 KB的数据)并等待一个往返,直到接收器发回对这些数据包的确认。 如果它们都被确认,这意味着网络可以处理该发送速率,我们可以尝试重复该过程,但需要更多数据(实际上,发送速率通常每次迭代都会翻倍)。

这样,发送速率会继续增长,直到某些数据包未被确认(这表明数据包丢失和网络拥塞)。 这个第一阶段通常被称为“慢启动”。 在检测到数据包丢失时,TCP 会降低发送速率,并且(一段时间后)开始再次增加发送速率,尽管增量(小得多)。 此后,每次丢包都会重复这种先减少后增长的逻辑。 最终,这意味着 TCP 将不断尝试达到其理想的、公平的带宽份额。 这种机制如图 1 所示。

TCP拥塞控制
图 1. TCP 拥塞控制的简化示例,从 10 个数据包的发送速率开始(改编自 hpbn.co。(大预览)

这是对拥塞控制的过度简化的解释。 在实践中,还有许多其他因素在起作用,例如缓冲区膨胀、由于拥塞导致的 RTT 波动,以及多个并发发送者需要获得其公平份额的带宽这一事实。 因此,存在许多不同的拥塞控制算法,并且今天仍在发明大量算法,但没有一种算法在所有情况下都表现最佳。

虽然 TCP 的拥塞控制使其健壮,但也意味着需要一段时间才能达到最佳发送速率,具体取决于 RTT 和实际可用带宽。 对于网页加载,这种慢启动方法也会影响诸如第一次内容绘制等指标,因为在最初的几次往返中只能传输少量数据(几十到几百 KB)。 (您可能听说过将关键数据保持在 14 KB 以下的建议。)

因此,选择更积极的方法可能会在高带宽和高延迟网络上产生更好的结果,尤其是在您不关心偶尔的数据包丢失的情况下。 这就是我再次看到许多关于 QUIC 工作原理的误解的地方。

正如第 1 部分所讨论的,QUIC 在理论上受到丢包(以及相关的线头 (HOL) 阻塞)的影响较小,因为它独立处理每个资源字节流上的丢包。 此外,QUIC 运行在用户数据报协议(UDP) 上,与 TCP 不同,它没有内置的拥塞控制功能; 它允许您尝试以您想要的任何速率发送,并且不会重新传输丢失的数据。

这导致许多文章声称 QUIC 也不使用拥塞控制,QUIC 可以改为通过 UDP 以更高的速率开始发送数据(依靠去除 HOL 阻塞来处理数据包丢失),这就是为什么QUIC 比 TCP 快得多。

实际上,事实并非如此: QUIC 实际上使用与 TCP 非常相似的带宽管理技术。 它也以较低的发送速率开始,并随着时间的推移而增长,使用确认作为衡量网络容量的关键机制。 这是(除其他原因外)因为 QUIC 需要可靠才能对诸如 HTTP 之类的东西有用,因为它需要对其他 QUIC(和 TCP!)连接公平,并且因为它的 HOL 阻塞删除不会实际上有助于防止数据包丢失(我们将在下面看到)。

然而,这并不意味着 QUIC 在管理带宽方面不能(有点)比 TCP 更聪明。 这主要是因为QUIC 比 TCP 更灵活、更容易演进。 正如我们所说,拥塞控制算法今天仍在大力发展,例如,我们可能需要调整一些东西以充分利用 5G。

但是,TCP 通常在操作系统 (OS') 内核中实现,这是一个安全且更受限制的环境,对于大多数操作系统来说甚至不是开源的。 因此,调整拥塞逻辑通常只由少数几个开发人员完成,而且发展缓慢。

相比之下,大多数 QUIC 实现目前是在“用户空间”(我们通常运行本地应用程序)中完成的,并且是开源的,明确鼓励更多开发人员进行实验(如 Facebook 所示) )。

另一个具体的例子是 QUIC 的延迟确认频率扩展提案。 虽然默认情况下,QUIC 每收到 2 个数据包发送一个确认,但此扩展允许端点确认,例如,每 10 个数据包。 这已被证明在卫星和超高带宽网络上具有很大的速度优势,因为传输确认数据包的开销降低了。 为 TCP 添加这样的扩展需要很长时间才能被采用,而对于 QUIC,部署起来要容易得多。

因此,我们可以预期 QUIC 的灵活性会随着时间的推移带来更多的实验和更好的拥塞控制算法,这反过来也可以反向移植到 TCP 以改进它。

你知道吗?

官方的 QUIC Recovery RFC 9002 指定使用 NewReno 拥塞控制算法。 虽然这种方法很健壮,但它也有些过时,不再在实践中广泛使用。 那么,为什么它在 QUIC RFC 中呢? 第一个原因是,当 QUIC 启动时,NewReno 是最新的拥塞控制算法,它本身是标准化的。 更高级的算法,例如 BBR 和 CUBIC,要么仍未标准化,要么最近才成为 RFC。

第二个原因是 NewReno 是一个相对简单的设置。 因为算法需要一些调整来处理 QUIC 与 TCP 的差异,所以用更简单的算法来解释这些变化会更容易。 因此,RFC 9002 应该更多地理解为“如何使拥塞控制算法适应 QUIC”,而不是“这是你应该为 QUIC 使用的东西”。 事实上,大多数生产级 QUIC 实现都对 Cubic 和 BBR 进行了自定义实现。

值得重申的是,拥塞控制算法不是特定于 TCP 或 QUIC 的 它们可以被任一协议使用,并且希望 QUIC 的进步最终也会找到通往 TCP 堆栈的方式。
你知道吗?

请注意,在拥塞控制旁边是一个相关的概念,称为流量控制。 这两个特性在 TCP 中经常被混淆,因为它们都被称为使用“TCP 窗口” ,尽管实际上有两个窗口:拥塞窗口和 TCP 接收窗口。 然而,对于我们感兴趣的网页加载用例,流控制发挥的作用要少得多,所以我们将在此处跳过它。 提供更深入的信息。

这是什么意思呢?

QUIC 仍然受物理定律的约束,并且需要对互联网上的其他发件人友善。 这意味着它不会比 TCP 更快地神奇地下载您的网站资源。 然而,QUIC 的灵活性意味着试验新的拥塞控制算法将变得更加容易,这将在未来改善 TCP 和 QUIC 的情况。

0-RTT 连接设置

第二个性能方面是关于在新连接上发送有用的 HTTP 数据(例如,页面资源)之前需要多少次往返。 有人声称 QUIC 比 TCP + TLS 快两到三个往返,但我们会看到它实际上只有一个。

你知道吗?

正如我们在第 1 部分中所说,连接通常会在 HTTP 请求和响应可以交换之前执行一次 (TCP) 或两次 (TCP + TLS) 握手。 这些握手交换客户端和服务器都需要知道的初始参数,以便例如加密数据。

正如您在下面图 2 中所见,每次握手至少需要一次往返才能完成(TCP + TLS 1.3,(b)),有时需要两次(TLS 1.2 和之前的 (a))。 这是低效的,因为我们需要至少两次握手等待时间(开销)才能发送我们的第一个 HTTP 请求,这意味着至少要等待三个往返才能收到第一个 HTTP 响应数据(返回的红色箭头)在慢速网络上,这可能意味着 100 到 200 毫秒的开销。
TCP拥塞控制
图 2:TCP + TLS 与 QUIC 连接设置。 (大预览)

您可能想知道为什么 TCP + TLS 握手不能简单地组合在一起,在同一次往返中完成。 虽然这在概念上是可能的(QUIC 正是这样做的),但最初并不是这样设计的,因为我们需要能够在顶部使用带或不带 TLS 的 TCP。 换句话说,TCP 根本不支持在握手期间发送非 TCP 内容。 已经努力通过 TCP Fast Open 扩展来添加它; 然而,正如第 1 部分中所讨论的,事实证明这很难大规模部署。

幸运的是,QUIC 从一开始就考虑到了 TLS,因此将传输和加密握手结合在一个机制中。 这意味着 QUIC 握手总共只需要一次往返即可完成,比 TCP + TLS 1.3 少一次往返(参见上面的图 2c)。

您可能会感到困惑,因为您可能已经读过 QUIC 比 TCP 快两甚至三次往返,而不仅仅是一次。 这是因为大多数文章只考虑最坏的情况(TCP + TLS 1.2,(a)),更不用说现代 TCP + TLS 1.3 也“只”需要两次往返((b)很少显示)。 虽然一次往返的速度提升很好,但这并不令人惊讶。 尤其是在快速网络上(例如,小于 50 毫秒的 RTT),这几乎不会引起注意,尽管慢速网络和与远程服务器的连接会带来更多收益。

接下来,您可能想知道为什么我们需要等待握手。 为什么我们不能在第一次往返中发送 HTTP 请求? 这主要是因为,如果我们这样做了,那么第一个请求将被发送未加密,任何窃听者都可以读取,这显然不利于隐私和安全。 因此,在发送第一个 HTTP 请求之前,我们需要等待加密握手完成。 还是我们?

这是在实践中使用巧妙技巧的地方。 我们知道,用户经常在第一次访问后的短时间内重新访问网页。 因此,我们可以在未来使用初始加密连接来引导第二个连接。 简而言之,在其生命周期的某个时候,第一个连接用于在客户端和服务器之间安全地传递新的密码参数。 然后可以使用这些参数从一开始就加密第二个连接,而无需等待完整的 TLS 握手完成。 这种方法称为“会话恢复”

它允许进行强大的优化:我们现在可以安全地发送我们的第一个 HTTP 请求以及 QUIC/TLS 握手,节省另一个往返行程! 对于 TLS 1.3,这有效地消除了 TLS 握手的等待时间。 这种方法通常称为 0-RTT(当然,HTTP 响应数据开始到达仍需要往返一次)。

再次,会话恢复和 0-RTT 都是我经常看到的被错误地解释为 QUIC 特定功能的东西。 实际上,这些实际上是TLS 功能,在 TLS 1.2 中已经以某种形式存在,现在在 TLS 1.3 中完全成熟。

换句话说,正如您在下面的图 3 中看到的那样,我们也可以通过 TCP(以及 HTTP/2 甚至 HTTP/1.1)获得这些功能的性能优势! 我们看到,即使使用 0-RTT,QUIC 仍然只比运行最佳的 TCP + TLS 1.3 堆栈快一个往返。 QUIC 比图 2 的 (a) 和图 3 的 (f) 快三个往返的说法,正如我们所见,这并不公平。

TCP + TLS 与 QUIC 0-RTT 连接设置。
图 3:TCP + TLS 与 QUIC 0-RTT 连接设置。 (大预览)

最糟糕的是,当使用 0-RTT 时,由于安全性,QUIC 甚至不能真正使用获得的往返行程。 要理解这一点,我们需要了解 TCP 握手存在的原因之一。 首先,它允许客户端在向其发送任何更高层数据之前确保服务器在给定 IP 地址处实际可用。

其次,也是至关重要的一点,它允许服务器确保打开连接的客户端实际上是他们在发送数据之前所说的身份和位置。 如果您还记得我们在第 1 部分中如何定义与 4 元组的连接,您就会知道客户端主要由其 IP 地址标识。 这就是问题所在: IP 地址可以被欺骗

假设攻击者通过 QUIC 0-RTT 上的 HTTP 请求一个非常大的文件。 然而,他们欺骗了他们的 IP 地址,使得 0-RTT 请求看起来像是来自受害者的计算机。 如下图 4 所示。 QUIC 服务器无法检测 IP 是否被欺骗,因为这是它从该客户端看到的第一个数据包。

对受害者的 0-RTT 放大攻击。
图 4:攻击者可以在向 QUIC 服务器发送 0-RTT 请求时欺骗他们的 IP 地址,从而触发对受害者的放大攻击。 (大预览)

如果服务器然后只是开始将大文件发送回欺骗 IP,它最终可能会使受害者的网络带宽过载(特别是如果攻击者要并行执行许多这些虚假请求)。 请注意,受害者会丢弃 QUIC 响应,因为它不期望传入数据,但这并不重要:他们的网络仍然需要处理数据包!

这称为反射或放大攻击,它是黑客执行分布式拒绝服务 (DDoS) 攻击的一种重要方式。 请注意,当使用 0-RTT over TCP + TLS 时不会发生这种情况,正是因为 TCP 握手需要先完成,然后 0-RTT 请求甚至与 TLS 握手一起发送。

因此, QUIC 必须保守地回复 0-RTT 请求,限制它发送的响应数据量,直到客户端被验证为真正的客户端而不是受害者。 对于 QUIC,此数据量已设置为从客户端接收到的数据量的三倍。

换句话说,QUIC 的最大“放大系数”为 3,这被确定为性能有用性和安全风险之间可接受的权衡(特别是与放大系数超过 51,000 倍的某些事件相比)。 因为客户端通常首先只发送一到两个数据包,所以 QUIC 服务器的 0-RTT 回复将被限制在 4 到 6 KB (包括其他 QUIC 和 TLS 开销!),这并不令人印象深刻。

此外,其他安全问题可能会导致例如“重放攻击”,这会限制您可以执行的 HTTP 请求的类型。 例如,Cloudflare 仅允许 0-RTT 中不带查询参数的 HTTP GET 请求。 这些进一步限制了 0-RTT 的实用性。

幸运的是,QUIC 可以选择让它变得更好。 例如,服务器可以检查 0-RTT 是否来自之前与其有效连接的 IP。 但是,这仅在客户端保持在同一网络上时才有效(在某种程度上限制了 QUIC 的连接迁移功能)。 而且即使它有效,QUIC 的响应仍然受到我们上面讨论的拥塞控制器的慢启动逻辑的限制; 因此,除了节省的一次往返行程之外,没有额外的大幅速度提升

你知道吗?

有趣的是,QUIC 的三倍放大限制也适用于图 2c 中的正常非 0-RTT 握手过程。 例如,如果服务器的 TLS 证书太大而无法容纳 4 到 6 KB,这可能会成为问题。 在这种情况下,它必须被拆分,第二个块必须等待第二次往返发送(在确认前几个数据包进入后,表明客户端的 IP 没有被欺骗)。 在这种情况下, QUIC 的握手可能仍然需要两次往返,等于 TCP + TLS! 这就是为什么对于 QUIC 来说,证书压缩等技术将格外重要。
你知道吗?

某些高级设置可能足以缓解这些问题,从而使 0-RTT 更有用。 例如,服务器可以记住客户端上次看到它时有多少可用带宽,从而减少拥塞控制对重新连接(非欺骗)客户端的缓慢启动的限制。 这已经在学术界进行了调查,甚至在 QUIC 中提出了扩展来做到这一点。 几家公司也已经在做这种事情来加速 TCP。

另一种选择是让客户端发送超过一个或两个数据包(例如,再发送 7 个带有填充的数据包),因此即使在连接迁移之后,三倍的限制也会转化为更有趣的 12 到 14 KB 响应。 我已经在我的一篇论文中写到了这一点。

最后,(行为不端的)QUIC 服务器也可以故意增加三倍的限制,如果他们认为这样做是安全的,或者他们不关心潜在的安全问题(毕竟,没有协议警察阻止这一点)。

这是什么意思呢?

QUIC 使用0-RTT 的更快连接设置实际上更像是一种微优化,而不是一项革命性的新功能。 与最先进的 TCP + TLS 1.3 设置相比,它最多可以节省一次往返。 在第一次往返中实际可以发送的数据量还受到许多安全考虑的限制。

因此,如果您的用户在具有非常高延迟的网络(例如,具有超过 200 毫秒 RTT 的卫星网络)上,或者如果您通常不发送太多数据,则此功能将主要发挥作用。 后者的一些例子是大量缓存的网站,以及通过 API 和其他协议(如 DNS-over-QUIC)定期获取小更新的单页应用程序。 谷歌看到 QUIC 的 0-RTT 结果非常好的原因之一是它在其已经高度优化的搜索页面上对其进行了测试,其中查询响应非常小。

在其他情况下,您最多只能获得几十毫秒,如果您已经在使用 CDN(如果您关心性能,则应该这样做!)。

连接迁移

通过保持现有连接完好无损,第三个性能特性使 QUIC 在网络之间传输时更快。 虽然这确实有效,但这种类型的网络更改并不经常发生,连接仍然需要重置其发送速率。

如第 1 部分所述,QUIC 的连接 ID (CID) 允许它在切换网络时执行连接迁移。 我们通过一个客户端从 Wi-Fi 网络移动到 4G 并同时下载大文件来说明这一点。 在 TCP 上,下载可能必须中止,而对于 QUIC,它可能会继续。

但是,首先要考虑这种情况实际发生的频率。 您可能会认为,在建筑物内的 Wi-Fi 接入点之间或在路上的蜂窝塔之间移动时也会发生这种情况。 然而,在这些设置中(如果正确完成),您的设备通常会保持其 IP 不变,因为无线基站之间的转换是在较低的协议层完成的。 因此,它仅在您在完全不同的网络之间移动时才会发生,我想说这种情况并不经常发生。

其次,我们可以询问这是否也适用于除大文件下载和实时视频会议和流媒体之外的其他用例。 如果您在切换网络的确切时刻加载网页,您可能确实需要重新请求一些(后来的)资源。

但是,加载页面通常需要几秒钟的时间,因此与网络切换同时发生的情况也不会很常见。 此外,对于这是一个紧迫问题的用例,其他缓解措施通常已经到位。 例如,提供大文件下载的服务器可以支持 HTTP 范围请求以允许可恢复下载。

由于网络 1 断开和网络 2 可用之间通常有一些重叠时间,因此视频应用程序可以打开多个连接(每个网络 1 个),在旧网络完全消失之前同步它们。 用户仍会注意到这个开关,但它不会完全放弃视频源。

第三,不能保证新网络的可用带宽与旧网络一样多。 因此,即使概念连接保持不变,QUIC 服务器也不能仅仅保持高速发送数据。 相反,为了避免新网络过载,它需要重置(或至少降低)发送速率并在拥塞控制器的慢启动阶段重新启动。

因为这个初始发送速率通常太低而无法真正支持诸如视频流之类的东西,所以即使在 QUIC 上,您也会看到一些质量损失或打嗝。 在某种程度上,连接迁移更多的是为了防止连接上下文混乱和服务器上的开销,而不是提高性能。

你知道吗?

请注意,正如上面针对 0-RTT 所讨论的,我们可以设计一些高级技术来改进连接迁移。 例如,我们可以再次尝试记住上次给定网络上有多少可用带宽,并尝试更快地提升到该级别以进行新的迁移。 此外,我们可以设想不只是在网络之间切换,而是同时使用两者。 这个概念称为多路径,我们将在下面更详细地讨论它。

到目前为止,我们主要讨论了主动连接迁移,即用户在不同网络之间移动。 但是,也存在被动连接迁移的情况,其中某个网络本身会更改参数。 一个很好的例子是网络地址转换 (NAT) 重新绑定。 虽然对 NAT 的全面讨论超出了本文的范围,但它主要意味着连接的端口号可以在任何给定时间更改,而不会发出警告。 在大多数路由器中,UDP 比 TCP 更经常发生这种情况。

如果发生这种情况,QUIC CID 不会改变,并且大多数实现会假设用户仍然在同一个物理网络上,因此不会重置拥塞窗口或其他参数。 QUIC 还包括一些功能,例如 PING 和超时指示器,以防止这种情况发生,因为这通常发生在长空闲连接中。

我们在第 1 部分讨论了 QUIC 不只是出于安全原因使用单个 CID。 相反,它会在执行主动迁移时更改 CID。 在实践中,它甚至更复杂,因为客户端和服务器都有单独的 CID 列表(在 QUIC RFC 中称为源和目标 CID)。 这在下面的图 5 中进行了说明。

QUIC 使用单独的源 CID 和目标 CID
图 5:QUIC 使用单独的客户端和服务器 CID。 (大预览)

这样做是为了允许每个端点选择自己的 CID 格式和内容,这反过来对于允许高级路由和负载平衡逻辑至关重要。 通过连接迁移,负载均衡器不再仅仅查看 4 元组来识别连接并将其发送到正确的后端服务器。 但是,如果所有 QUIC 连接都使用随机 CID,这将大大增加负载均衡器的内存需求,因为它需要存储 CID 到后端服务器的映射。 此外,这仍然不适用于连接迁移,因为 CID 会更改为新的随机值。

因此,部署在负载均衡器后面的 QUIC 后端服务器具有可预测的 CID 格式非常重要,这样负载均衡器即使在迁移之后也可以从 CID 派生正确的后端服务器。 IETF 的提议文件中描述了一些用于执行此操作的选项。 为了使这一切成为可能,服务器需要能够选择他们自己的 CID,如果连接发起者(对于 QUIC 来说,它始终是客户端)选择了 CID,这是不可能的。 这就是 QUIC 中客户端和服务器 CID 之间存在分裂的原因。

这是什么意思呢?

因此,连接迁移是一种情境特征。 例如,谷歌的初步测试显示其用例的改进百分比很低。 许多 QUIC 实现还没有实现这个特性。 即使是那些这样做的人,通常也会将其限制为移动客户端和应用程序,而不是它们的桌面等价物。 有些人甚至认为不需要该功能,因为在大多数情况下,使用 0-RTT 打开新连接应该具有相似的性能属性。

尽管如此,根据您的用例或用户资料,它可能会产生很大的影响。 如果您的网站或应用程序最常在移动中使用(例如,Uber 或 Google 地图之类的),那么您可能会比您的用户通常坐在办公桌后获得更多好处。 Similarly, if you're focusing on constant interaction (be it video chat, collaborative editing, or gaming), then your worst-case scenarios should improve more than if you have a news website.

Head-of-Line Blocking Removal

The fourth performance feature is intended to make QUIC faster on networks with a high amount of packet loss by mitigating the head-of-line (HoL) blocking problem. While this is true in theory, we will see that in practice this will probably only provide minor benefits for web-page loading performance.

To understand this, though, we first need to take a detour and talk about stream prioritization and multiplexing.

Stream Prioritization

As discussed in part 1, a single TCP packet loss can delay data for multiple in-transit resources because TCP's bytestream abstraction considers all data to be part of a single file. QUIC, on the other hand, is intimately aware that there are multiple concurrent bytestreams and can handle loss on a per-stream basis. However, as we've also seen, these streams are not truly transmitting data in parallel: Rather, the stream data is multiplexed onto a single connection. This multiplexing can happen in many different ways.

For example, for streams A, B, and C, we might see a packet sequence of ABCABCABCABCABCABCABCABC , where we change the active stream in each packet (let's call this round-robin). However, we might also see the opposite pattern of AAAAAAAABBBBBBBBCCCCCCCC , where each stream is completed in full before starting the next one (let's call this sequential). Of course, many other options are possible in between these extremes ( AAAABBCAAAAABBC… , AABBCCAABBCC… , ABABABCCCC… , etc.). The multiplexing scheme is dynamic and driven by an HTTP-level feature called stream prioritization (discussed later in this article).

As it turns out, which multiplexing scheme you choose can have a huge impact on website loading performance. You can see this in the video below, courtesy of Cloudflare, as every browser uses a different multiplexer. The reasons why are quite complex, and I've written several academic papers on the topic, as well as talked about it in a conference. Patrick Meenan, of Webpagetest fame, even has a three-hour tutorial on just this topic.

Stream prioritization in different browsers
Stream multiplexing differences can have a large impact on website loading in different browsers. (大预览)

Luckily, we can explain the basics relatively easily. As you may know, some resources can be render blocking. This is the case for CSS files and for some JavaScript in the HTML head element. While these files are loading, the browser cannot paint the page (or, for example, execute new JavaScript).

What's more, CSS and JavaScript files need to be downloaded in full in order to be used (although they can often be incrementally parsed and compiled). As such, these resources need to be loaded as soon as possible, with the highest priority. Let's contemplate what would happen if A, B, and C were all render-blocking resources.

Render blocking multiplexing impact
Figure 6: The stream multiplexing approach affects (render-blocking) resource completion time. (大预览)

If we use a round-robin multiplexer (the top row in figure 6), we would actually delay each resource's total completion time, because they all need to share bandwidth with the others. Since we can only use them after they are fully loaded, this incurs a significant delay. However, if we multiplex them sequentially (the bottom row in figure 6), we would see that A and B complete much earlier (and can be used by the browser), while not actually delaying C's completion time.

However, that doesn't mean that sequential multiplexing is always the best, because some (mostly non-render-blocking) resources (such as HTML and progressive JPEGs) can actually be processed and used incrementally . In those (and some other) cases, it makes sense to use the first option (or at least something in between).

Still, for most web-page resources, it turns out that sequential multiplexing performs best . This is, for example, what Google Chrome is doing in the video above, while Internet Explorer is using the worst-case round-robin multiplexer.

Packet Loss Resilience

Now that we know that all streams aren't always active at the same time and that they can be multiplexed in different ways, we can consider what happens if we have packet loss. As explained in part 1, if one QUIC stream experiences packet loss, then other active streams can still be used (whereas, in TCP, all would be paused).

However, as we've just seen, having many concurrent active streams is typically not optimal for web performance, because it can delay some critical (render-blocking) resources, even without packet loss! We'd rather have just one or two active at the same time, using a sequential multiplexer. However, this reduces the impact of QUIC's HoL blocking removal.

Imagine, for example, that the sender could transmit 12 packets at a given time (see figure 7 below) — remember that this is limited by the congestion controller). If we fill all 12 of those packets with data for stream A (because it's high priority and render-blocking — think main.js ), then we would have only one active stream in that 12-packet window.

If one of those packets were to be lost, then QUIC would still end up fully HoL blocked because there would simply be no other streams it could process besides A : All of the data is for A , and so everything would still have to wait (we don't have B or C data to process), similar to TCP.

HoL blocking with round-robin versus sequential multiplexing
Figure 7: Packet loss impact depends on the multiplexer used. (Note that we assume each stream has more data to send than in the previous similar images. (Large preview)

We see that we have a kind of contradiction: Sequential multiplexing ( AAAABBBBCCCC ) is typically better for web performance, but it doesn't allow us to take much advantage of QUIC's HoL blocking removal. Round-robin multiplexing ( ABCABCABCABC ) would be better against HoL blocking, but worse for web performance. As such, one best practice or optimization can end up undoing another .

而且情况会变得更糟。 Up until now, we've sort of assumed that individual packets get lost one at a time. However, this isn't always true, because packet loss on the Internet is often “bursty”, meaning that multiple packets often get lost at the same time .

As discussed above, an important reason for packet loss is that a network is overloaded with too much data, having to drop excess packets. This is why the congestion controller starts sending slowly. However, it then keeps growing its send rate until… there is packet loss!

Put differently, the mechanism that's intended to prevent overloading the network actually overloads the network (albeit in a controlled fashion). On most networks, that occurs after quite a while, when the send rate has increased to hundreds of packets per round trip. When those reach the limit of the network, several of them are typically dropped together, leading to the bursty loss patterns.

Did You Know?

This is one of the reasons why we wanted to move to using a single (TCP) connection with HTTP/2, rather than the 6 to 30 connections with HTTP/1.1. Because each individual connection ramps up its send rate in pretty much the same way, HTTP/1.1 could get a good speed-up at the start, but the connections could actually start causing massive packet loss for each other as they caused the network to become overloaded.

At the time, Chromium developers speculated that this behaviour caused most of the packet loss seen on the Internet. This is also one of the reasons why BBR has become an often used congestion-control algorithm, because it uses fluctuations in observed RTTs, rather than packet loss, to assess available bandwidth.
Did You Know?

Other causes of packet loss can lead to fewer or individual packets becoming lost (or unusable), especially on wireless networks. There, however, the losses are often detected at lower protocol layers and solved between two local entities (say, the smartphone and the 4G cellular tower), rather than by retransmissions between the client and the server. These usually don't lead to real end-to-end packet loss, but rather show up as variations in packet latency (or “jitter”) and reordered packet arrivals.

So, let's say we are using a per-packet round-robin multiplexer ( ABCABCABCABCABCABCABCABC… ) to get the most out of HoL blocking removal, and we get a bursty loss of just 4 packets. We see that this will always impact all 3 streams (see figure 8, middle row)! In this case, QUIC's HoL blocking removal provides no benefits, because all streams have to wait for their own retransmissions .

Packet loss patterns and their interaction with different multiplexers
Figure 8: Depending on the multiplexer used and the packet loss pattern, more or fewer streams are affected. (大预览)

To lower the risk of multiple streams being affected by a lossy burst, we need to concatenate more data for each stream. For example, AABBCCAABBCCAABBCCAABBCC… is a small improvement, and AAAABBBBCCCCAAAABBBBCCCC… (see bottom row in figure 8 above) is even better. You can again see that a more sequential approach is better, even though that reduces the chances that we have multiple concurrent active streams.

In the end, predicting the actual impact of QUIC's HoL blocking removal is difficult, because it depends on the number of streams, the size and frequency of the loss bursts, how the stream data is actually used, etc. However, most results at this time indicate it will not help much for the use case of web-page loading, because there we typically want fewer concurrent streams.

If you want even more detail on this topic or just some concrete examples, please check out my in-depth article on HTTP HoL blocking.

Did You Know?

As with the previous sections, some advanced techniques can help us here. For example, modern congestion controllers use packet pacing. This means that they don't send, for example, 100 packets in a single burst, but rather spread them out over an entire RTT. This conceptually lowers the chances of overloading the network, and the QUIC Recovery RFC strongly recommends using it. Complementarily, some congestion-control algorithms such as BBR don't keep increasing their send rate until they cause packet loss, but rather back off before that (by looking at, for example, RTT fluctuations, because RTTs also rise when a network is becoming overloaded).

While these approaches lower the overall chances of packet loss, they don't necessarily lower its burstiness.

这是什么意思呢?

While QUIC's HoL blocking removal means, in theory, that it (and HTTP/3) should perform better on lossy networks, in practice this depends on a lot of factors. Because the use case of web-page loading typically favours a more sequential multiplexing set-up, and because packet loss is unpredictable, this feature would, again, likely affect mainly the slowest 1% of users . However, this is still a very active area of research, and only time will tell.

Still, there are situations that might see more improvements. These are mostly outside of the typical use case of the first full page load — for example, when resources are not render blocking, when they can be processed incrementally, when streams are completely independent, or when less data is sent at the same time.

Examples include repeat visits on well-cached pages and background downloads and API calls in single-page apps. For example, Facebook has seen some benefits from HoL blocking removal when using HTTP/3 to load data in its native app.

UDP 和 TLS 性能

QUIC 和 HTTP/3 的第五个性能方面是关于它们在网络上实际创建和发送数据包的效率和性能。 我们将看到 QUIC 对 UDP 和重度加密的使用可以使它比 TCP 慢一点(但情况正在改善)。

首先,我们已经讨论过 QUIC 对 UDP 的使用更多的是关于灵活性和可部署性,而不是性能。 直到最近,通过 UDP 发送 QUIC 数据包通常比发送 TCP 数据包慢得多的事实更加证明了这一点。 这部分是因为这些协议通常在何处以及如何实施(参见下面的图 9)。

TCP和QUIC的实现区别
图 9:TCP 和 QUIC 之间的实现差异。 (大预览)

如上所述,TCP 和 UDP 通常直接在操作系统的快速内核中实现。 相比之下,TLS 和 QUIC 实现大多在较慢的用户空间中(请注意,这对于 QUIC 来说并不是真正需要的——它主要是因为它更灵活而完成)。 这使得 QUIC 已经比 TCP 慢了一点。

此外,当从我们的用户空间软件(例如,浏览器和 Web 服务器)发送数据时,我们需要将这些数据传递给操作系统内核,然后使用 TCP 或 UDP 将其实际放到网络上。 传递这些数据是使用内核 API(系统调用)完成的,每个 API 调用都会产生一定的开销。 对于 TCP,这些开销远低于 UDP。

这主要是因为从历史上看,TCP 的使用比 UDP 多得多。 因此,随着时间的推移,许多优化被添加到 TCP 实现和内核 API 中,以将数据包发送和接收开销降至最低。 许多网络接口控制器 (NIC) 甚至具有用于 TCP 的内置硬件卸载功能。 然而,UDP 就没有那么幸运了,因为它更有限的使用并不能证明对额外优化的投资是合理的。 幸运的是,在过去五年中,这种情况发生了变化,大多数操作系统也为 UDP 添加了优化选项

其次,QUIC 有很多开销,因为它单独加密每个数据包。 这比在 TCP 上使用 TLS 慢,因为在那里您可以以块的形式加密数据包(一次最多大约 16 KB 或 11 个数据包),这样效率更高。 这是在 QUIC 中做出的有意识的权衡,因为批量加密会导致其自身形式的 HoL 阻塞。

与第一点不同,我们可以添加额外的 API 以使 UDP(以及因此 QUIC)更快,在这里,QUIC 总是比 TCP + TLS 具有固有的劣势。 然而,这在实践中也很容易管理,例如优化的加密库和允许批量加密 QUIC 数据包标头的巧妙方法。

结果,虽然谷歌最早的 QUIC 版本仍然比 TCP + TLS 慢两倍,但从那以后情况肯定有所改善。 例如,在最近的测试中,微软经过高度优化的 QUIC 堆栈能够达到 7.85 Gbps,而在同一系统上 TCP + TLS 的速度为 11.85 Gbps(因此,在这里,QUIC 的速度大约是 TCP + TLS 的 66%)。

这与最近的 Windows 更新有关,它使 UDP 更快(为了全面比较,该系统上的 UDP 吞吐量为 19.5 Gbps)。 谷歌 QUIC 堆栈的最优化版本目前比 TCP + TLS 慢 20% 左右。 Fastly 较早在一个不太先进的系统上进行的测试,通过一些技巧甚至声称具有相同的性能(约 450 Mbps),这表明根据用例,QUIC 绝对可以与 TCP 竞争。

然而,即使 QUIC 的速度是 TCP + TLS 的两倍,它也不是那么糟糕。 首先,QUIC 和 TCP + TLS 处理通常不是服务器上发生的最繁重的事情,因为其他逻辑(例如,HTTP、缓存、代理等)也需要执行。 因此,您实际上不需要两倍的服务器来运行 QUIC (不过,尚不清楚它将对真实数据中心产生多大影响,因为没有一家大公司发布过这方面的数据)。

其次,未来仍有很多优化 QUIC 实现的机会。 例如,随着时间的推移,一些 QUIC 实现将(部分)移动到操作系统内核(很像 TCP)或绕过它(有些已经这样做,如 MsQuic 和 Quant)。 我们还可以期待 QUIC 特定的硬件可用。

尽管如此,仍可能存在一些 TCP + TLS 仍将是首选选项的用例。 例如,Netflix 表示它可能不会很快迁移到 QUIC,因为它已经大量投资于自定义 FreeBSD 设置以通过 TCP + TLS 流式传输其视频。

同样,Facebook 曾表示 QUIC 可能主要用于最终用户和 CDN 的边缘之间,但不会在数据中心之间或边缘节点和源服务器之间使用,因为它的开销更大。 一般来说,非常高带宽的场景可能会继续偏爱 TCP + TLS,尤其是在未来几年。

你知道吗?

优化网络堆栈是一个深刻的技术兔子洞,上面的内容只是触及表面(并遗漏了很多细微差别)。 如果您足够勇敢,或者如果您想知道GRO/GSOSO_TXTIME 、内核绕过以及sendmmsg()recvmmsg()含义,我也可以推荐一些 Cloudflare 和 Fastly 关于优化 QUIC 的优秀文章作为 Microsoft 的广泛代码演练和 Cisco 的深入讨论。 最后,一位谷歌工程师做了一个非常有趣的主题演讲,关于随着时间的推移优化他们的 QUIC 实现。

这是什么意思呢?

QUIC 对 UDP 和 TLS 协议的特殊使用在历史上使其比 TCP + TLS 慢得多。 然而,随着时间的推移,已经做出了一些改进(并将继续实施),从而在一定程度上缩小了差距。 不过,在网页加载的典型用例中,您可能不会注意到这些差异,但如果您维护大型服务器场,它们可能会让您头疼。

HTTP/3 特性

到目前为止,我们主要讨论了 QUIC 与 TCP 的新性能特性。 但是,HTTP/3 与 HTTP/2 的区别是什么? 正如第 1 部分中所讨论的, HTTP/3 实际上是 HTTP/2-over-QUIC ,因此,新版本中没有引入真正的、大的新特性。 这与从 HTTP/1.1 到 HTTP/2 的转变不同,后者更大,并引入了诸如标头压缩、流优先级和服务器推送等新功能。 这些特性都仍然在 HTTP/3 中,但它们在底层实现的方式存在一些重要差异。

这主要是因为 QUIC 移除 HoL 阻塞的工作原理。 正如我们所讨论的,流 B 的丢失不再意味着流 A 和 C 将不得不等待 B 的重新传输,就像他们通过 TCP 所做的那样。 因此,如果 A、B 和 C 各自按该顺序发送一个 QUIC 数据包,则它们的数据很可能会以 A、C、B 的形式传送到浏览器(并由浏览器处理)! 换句话说,与 TCP 不同,QUIC不再在不同的流中完全排序

这是 HTTP/2 的问题,它在设计许多特性时真正依赖于 TCP 的严格排序,这些特性使用散布在数据块中的特殊控制消息。 在 QUIC 中,这些控制消息可能以任何顺序到达(并被应用),甚至可能使功能与预期相反! 同样,本文不需要技术细节,但本文的前半部分应该让您了解这会变得多么愚蠢复杂。

因此,必须针对 HTTP/3 更改功能的内部机制和实现。 一个具体的例子是HTTP 标头压缩,它降低了重复的大型 HTTP 标头(例如,cookie 和用户代理字符串)的开销。 在 HTTP/2 中,这是使用 HPACK 设置完成的,而对于 HTTP/3,这已被重新设计为更复杂的 QPACK。 两个系统都提供相同的功能(即标头压缩),但方式完全不同。 可以在 Litespeed 博客上找到有关此主题的一些出色的深入技术讨论和图表。

驱动流多路复用逻辑的优先级特性也是如此,我们在上面已经简要讨论过。 在 HTTP/2 中,这是使用复杂的“依赖树”设置实现的,该设置明确地尝试对所有页面资源及其相互关系进行建模(更多信息请参见“HTTP 资源优先级的终极指南”)。 直接在 QUIC 上使用这个系统会导致一些潜在的非常错误的树布局,因为将每个资源添加到树中将是一个单独的控制消息。

此外,这种方法被证明是不必要的复杂,导致许多实施错误和效率低下以及许多服务器上的性能不佳。 这两个问题都导致以更简单的方式为 HTTP/3 重新设计优先级系统。 这种更直接的设置使得一些高级场景难以或不可能强制执行(例如,在单个连接上代理来自多个客户端的流量),但仍然为网页加载优化提供了广泛的选项。

同样,这两种方法提供相同的基本功能(引导流多路复用),但希望 HTTP/3 更简单的设置将减少实现错误。

最后,还有服务器推送。 此功能允许服务器发送 HTTP 响应,而无需先等待明确的请求。 从理论上讲,这可以带来出色的性能提升。 然而,在实践中,事实证明它很难正确使用且实施不一致。 因此,它甚至可能会从 Google Chrome 中删除。

尽管如此,它仍然被定义为 HTTP/3 中的一个特性(尽管很少有实现支持它)。 虽然它的内部工作方式没有像前两个功能那样发生很大变化,但它也已经适应了 QUIC 的非确定性排序。 但遗憾的是,这对于解决其一些长期存在的问题无济于事。

这是什么意思呢?

正如我们之前所说,HTTP/3 的大部分潜力来自底层的 QUIC,而不是 HTTP/3 本身。 虽然该协议的内部实现与 HTTP/2非常不同,但它的高级性能特性以及它们可以和应该如何使用都保持不变。

值得关注的未来发展

在本系列文章中,我经常强调更快的演进和更高的灵活性是 QUIC(以及扩展的 HTTP/3)的核心方面。 因此,人们已经在研究协议的新扩展和应用也就不足为奇了。 下面列出的是您可能会在某处遇到的主要问题:

  • 前向纠错
    再次,该技术的目的是提高 QUIC 对丢包的恢复能力。 它通过发送数据的冗余副本来做到这一点(尽管经过巧妙的编码和压缩,因此它们没有那么大)。 然后,如果数据包丢失但冗余数据到达,则不再需要重传。

    这最初是 Google QUIC 的一部分(也是人们说 QUIC 可以很好地防止丢包的原因之一),但它没有包含在标准化的 QUIC 版本 1 中,因为它的性能影响尚未得到证实。 不过,研究人员现在正在使用它进行积极的实验,您可以使用 PQUIC-FEC 下载实验应用程序帮助他们。

  • 多路径 QUIC
    我们之前已经讨论过连接迁移以及从 Wi-Fi 迁移到蜂窝网络时它如何提供帮助。 但是,这是否也意味着我们可以同时使用 Wi-Fi 和蜂窝网络? 同时使用这两个网络将为我们提供更多可用带宽和更高的鲁棒性! 这是多路径背后的主要概念。

    这也是谷歌尝试过的东西,但由于其固有的复杂性,它没有进入 QUIC 版本 1。 然而,研究人员已经展示了它的巨大潜力,它可能会成为 QUIC 版本 2。请注意,TCP 多路径也存在,但它已经花了将近十年的时间才变得实用。

  • QUIC 和 HTTP/3 上的不可靠数据
    正如我们所见,QUIC 是一个完全可靠的协议。 但是,由于它运行在不可靠的 UDP 上,我们可以在 QUIC 中添加一个功能来发送不可靠的数据。 这在建议的数据报扩展中进行了概述。 你当然不想用它来发送网页资源,但它可能对游戏和实时视频流等事情很方便。 这样,用户将获得 UDP 的所有好处,但具有 QUIC 级加密和(可选)拥塞控制。

  • 网络传输
    浏览器不会将 TCP 或 UDP 直接暴露给 JavaScript,主要是出于安全考虑。 相反,我们必须依赖 HTTP 级别的 API,例如 Fetch 以及更灵活的 WebSocket 和 WebRTC 协议。 这一系列选项中最新的称为 WebTransport,它主要允许您以更底层的方式使用 HTTP/3(以及扩展的 QUIC)(尽管如果需要它也可以回退到 TCP 和 HTTP/2 )。

    至关重要的是,它将包括通过 HTTP/3 使用不可靠数据的能力(请参阅前一点),这将使诸如游戏之类的事情在浏览器中更容易实现。 对于普通的 (JSON) API 调用,您当然仍会使用 Fetch,它还会在可能的情况下自动使用 HTTP/3。 WebTransport 目前仍在大量讨论中,因此尚不清楚它最终会是什么样子。 在这些浏览器中,目前只有 Chromium 正在开发一个公开的概念验证实现。

  • DASH 和 HLS 视频流
    对于非实时视频(想想 YouTube 和 Netflix),浏览器通常使用基于 HTTP 的动态自适应流式传输 (DASH) 或 HTTP 实时流式传输 (HLS) 协议。 两者基本上都意味着您将视频编码为更小的块(2 到 10 秒)和不同的质量级别(720p、1080p、4K 等)。

    在运行时,浏览器估计您的网络可以处理的最高质量(或给定用例的最佳质量),并通过 HTTP 从服务器请求相关文件。 因为浏览器不能直接访问 TCP 堆栈(通常在内核中实现),所以它偶尔会在这些估计中犯一些错误,或者需要一段时间才能对不断变化的网络条件做出反应(导致视频停顿) .

    因为 QUIC 是作为浏览器的一部分实现的,所以可以通过让流估计器访问低级协议信息(例如丢失率、带宽估计等)来大大改善这一点。 其他研究人员也一直在尝试将可靠和不可靠的数据混合用于视频流,并取得了一些有希望的结果。

  • HTTP/3 以外的协议
    由于 QUIC 是一种通用传输协议,我们可以预期现在在 TCP 上运行的许多应用层协议也将在 QUIC 之上运行。 一些正在进行的工作包括 DNS-over-QUIC、SMB-over-QUIC,甚至 SSH-over-QUIC。 因为这些协议通常具有与 HTTP 和网页加载非常不同的要求,所以我们讨论过的 QUIC 的性能改进可能对这些协议更有效。

这是什么意思呢?

QUIC 版本 1只是一个开始。 谷歌早先尝试过的许多面向性能的高级功能并没有进入第一次迭代。 然而,目标是快速发展协议,以高频率引入新的扩展和特性。 因此,随着时间的推移,QUIC(和 HTTP/3)应该明显比 TCP(和 HTTP/2)更快、更灵活。

结论

在本系列的第二部分中,我们讨论了HTTP/3 尤其是 QUIC 的许多不同性能特性和方面。 我们已经看到,虽然这些功能中的大多数看起来非常有影响力,但实际上,在我们一直在考虑的网页加载用例中,它们可能对普通用户没有那么大的作用。

例如,我们已经看到QUIC使用UDP并不意味着它可以突然使用比TCP更多的带宽,也不意味着它可以更快地下载你的资源。 经常受到称赞的 0-RTT 功能实际上是一种微优化,可以为您节省一次往返,其中您可以发送大约 5 KB(在最坏的情况下)。

如果出现突发数据包丢失或加载渲染阻塞资源时,HoL 阻塞删除效果不佳。 连接迁移是高度情境化的,HTTP/3 没有任何重要的新功能可以使其比 HTTP/2 更快。

因此,您可能希望我建议您跳过 HTTP/3 和 QUIC。 为什么要打扰,对吧? 但是,我绝对不会做这样的事情! 尽管这些新协议可能对快速(城市)网络上的用户没有多大帮助,但新功能确实有可能对高度移动的用户和慢速网络上的人产生巨大影响。

即使在像我自己的比利时这样的西方市场,我们通常拥有快速设备并可以访问高速蜂窝网络,这些情况也会影响 1% 甚至 10% 的用户群,具体取决于您的产品。 一个例子是火车上的某人拼命地试图在您的网站上查找一条关键信息,但必须等待 45 秒才能加载。 我当然知道我一直处于这种情况,希望有人部署了 QUIC 来让我摆脱困境。

但是,还有其他国家和地区的情况要糟糕得多。 在那里,普通用户可能看起来更像比利时最慢的 10%,而最慢的 1% 可能根本看不到加载的页面。 在世界许多地方,Web 性能是一个可访问性和包容性问题。

这就是为什么我们永远不应该只在我们自己的硬件上测试我们的页面(还要使用像 Webpagetest 这样的服务),也是为什么你绝对应该部署 QUIC 和 HTTP/3的原因。 特别是如果您的用户经常在移动或不太可能访问快速蜂窝网络,这些新协议可能会带来不同的世界,即使您在有线 MacBook Pro 上没有注意到太多。 有关更多详细信息,我强烈推荐 Fastly 关于该问题的帖子。

如果这不能完全说服您,那么请考虑一下 QUIC 和 HTTP/3 将在未来几年继续发展并变得更快。 获得一些协议的早期经验将在未来获得回报,让您尽快获得新功能的好处。 此外,QUIC 在后台执行安全和隐私最佳实践,这使世界各地的所有用户受益。

终于信服了? 然后继续阅读本系列的第 3 部分,了解如何在实践中使用新协议。

  • 第 1 部分:HTTP/3 历史和核心概念
    本文主要针对 HTTP/3 和一般协议的新手,主要讨论基础知识。
  • 第 2 部分:HTTP/3 性能特性
    这一个更深入和技术。 已经了解基础知识的人可以从这里开始。
  • 第 3 部分:实用的 HTTP/3 部署选项
    本系列的第三篇文章解释了自己部署和测试 HTTP/3 所涉及的挑战。 它详细说明了如何以及是否应该更改您的网页和资源。