HTTP/3 从头到尾:核心概念(第 1 部分)

2021-08-09 22:02:56

您可能已经阅读了一些博客文章或听过有关此主题的会议演讲,并认为您知道答案。你可能听说过这样的话:“当有数据包丢失时,HTTP/3 比 HTTP/2 快得多”,或者“HTTP/3 连接具有更少的延迟和更少的设置时间”,并且可能“HTTP/3可以更快地发送数据并可以并行发送更多资源”。这些陈述和文章通常会跳过一些关键的技术细节,缺乏细微差别,而且通常只是部分正确。他们常常让 HTTP/3 看起来像是一场性能革命,但它实际上是一个更温和(但仍然有用!)的演变。这是危险的,因为新协议在实践中可能无法满足这些高期望。我担心这会导致许多人最终感到失望,并使新来者被大量盲目长期存在的错误信息所迷惑。我很害怕这一点,因为我们已经看到 HTTP/2 发生了完全相同的情况。它被誉为令人惊叹的性能革命,具有令人兴奋的新功能,例如服务器推送、并行流和优先级。我们本可以停止捆绑资源,停止在多个服务器之间分片我们的资源,并大大简化页面加载过程。只需轻轻一按,网站速度就会神奇地提高 50%!五年后,我们知道服务器推送在实践中并没有真正起作用,流和优先级经常被错误地实现,因此,(减少的)资源捆绑甚至分片在某些情况下仍然是很好的做法。同样,其他调整协议行为的机制,例如预加载提示,通常包含隐藏的深度和错误,使其难以正确使用。因此,我认为防止此类错误信息和这些不切实际的期望也传播到 HTTP/3 非常重要。在本系列文章中,我将详细讨论新协议,尤其是它的性能特征。我将证明,虽然 HTTP/3 确实有一些有前途的新概念,但遗憾的是,它们对大多数网页和用户的影响可能相对有限(但对一小部分可能至关重要)。 HTTP/3 的设置和使用(正确)也非常具有挑战性,因此在配置新协议时要小心。

HTTP/3 历史和核心概念 这是针对 HTTP/3 和一般协议的新手,主要讨论基础知识。 HTTP/3 性能特性(即将推出!)这是更深入和技术性的。已经了解基础的人可以从这里开始。实用的 HTTP/3 部署选项(即将推出!)这解释了自己部署和测试 HTTP/3 所涉及的挑战。它详细说明了如何以及是否应该更改您的网页和资源。本系列主要面向对协议没有深入了解但希望了解更多信息的 Web 开发人员。但是,它确实包含足够的技术细节和许多外部资源的链接,以供更高级的读者感兴趣。我经常遇到的一个问题是,“为什么我们在 2015 年才标准化的 HTTP/2 之后这么快就需要 HTTP/3?”这确实很奇怪,直到您意识到我们实际上并不需要一个新的 HTTP 版本,而是底层传输控制协议 (TCP) 的升级。 TCP 是主要协议,它提供关键服务,如可靠性和向其他协议(如 HTTP)的有序交付。这也是我们可以继续与许多并发用户一起使用 Internet 的原因之一,因为它巧妙地将每个用户的带宽使用限制为他们的公平份额。你知道吗?使用 HTTP(S) 时,您实际上是在同时使用除 HTTP 之外的多种协议。这个“堆栈”中的每个协议都有自己的特性和职责(见下图)。例如,当 HTTP 处理 URL 和数据解释时,传输层安全 (TLS) 通过加密确保安全,TCP 通过重新传输丢失的数据包来实现可靠的数据传输,而互联网协议 (IP) 将数据包从一个端点路由到另一个跨不同设备的端点之间(中间盒)。

这种在协议之上的“分层”是为了允许轻松重用它们的功能。高层协议(如 HTTP)不必重新实现复杂的功能(如加密),因为低层协议(如 TLS)已经为它们做这些了。再举一个例子,Internet 上的大多数应用程序在内部使用 TCP 来确保它们的所有数据都得到完整传输。因此,TCP 是 Internet 上使用和部署最广泛的协议之一。几十年来,TCP 一直是网络的基石,但它在 2000 年代后期开始老化。它的预期替代品是一种名为 QUIC 的新传输协议,它在一些关键方面与 TCP 有很大不同,直接在其上运行 HTTP/2 将非常困难。因此,HTTP/3 本身是 HTTP/2 的一个相对较小的改编,以使其与新的 QUIC 协议兼容,其中包括人们兴奋的大部分新功能。需要 QUIC 是因为从互联网的早期就存在的 TCP 并没有真正考虑到最大效率。例如,TCP 需要“握手”来建立新连接。这样做是为了确保客户端和服务器都存在并且他们愿意并且能够交换数据。但是,它也需要一个完整的网络往返才能完成,然后才能在连接上完成任何其他操作。如果客户端和服务器在地理上相距遥远,则每个往返时间 (RTT) 可能需要 100 多毫秒,从而导致明显的延迟。作为第二个例子,TCP 将其传输的所有数据视为单个“文件”或字节流,即使我们实际上使用它同时传输多个文件(例如,当下载由以下内容组成的网页时)很多资源)。实际上,这意味着如果包含单个文件数据的 TCP 数据包丢失,那么所有其他文件也将延迟,直到这些数据包被恢复。这称为队头 (HoL) 阻塞。虽然这些低效率在实践中是可以控制的(否则,我们将不会使用 TCP 超过 30 年),但它们确实以明显的方式影响了更高级别的协议,例如 HTTP。随着时间的推移,我们尝试发展和升级 TCP 以改善其中一些问题,甚至引入新的性能特性。例如,TCP Fast Open 通过允许高层协议从一开始就发送数据来消除握手开销。另一项努力称为多路径 TCP。在这里,我们的想法是您的手机通常同时具有 Wi-Fi 和 (4G) 蜂窝连接,那么为什么不同时使用它们以获得额外的吞吐量和稳健性呢?实现这些 TCP 扩展并不是非常困难。然而,在互联网规模上实际部署它们是极具挑战性的。因为 TCP 非常流行,几乎每个连接的设备都有自己的协议实现。如果这些实现太旧、缺乏更新或有问题,那么这些扩展将无法实际使用。换句话说,所有实现都需要了解扩展才能使其有用。

如果我们只讨论最终用户设备(例如您的计算机或 Web 服务器),这不会是什么大问题,因为这些设备可以相对容易地手动更新。然而,许多其他设备位于客户端和服务器之间,它们也有自己的 TCP 代码(例如防火墙、负载平衡器、路由器、缓存服务器、代理等)。这些中间件通常更难更新,有时它们接受的内容更严格。例如,如果设备是防火墙,它可能被配置为阻止所有包含(未知)扩展的流量。在实践中,事实证明,大量活跃的中间件对 TCP 做出了某些假设,这些假设不再适用于新的扩展。因此,在足够多的(中间盒)TCP 实现更新以实际大规模使用扩展之前,可能需要数年甚至十多年的时间。可以说,发展 TCP 几乎是不可能的。因此,很明显我们需要 TCP 的替代协议,而不是直接升级,以解决这些问题。然而,由于 TCP 的特性及其各种实现的绝对复杂性,从头开始创建新的但更好的东西将是一项艰巨的任务。因此,在 2010 年代初,决定推迟这项工作。毕竟,不仅 TCP 存在问题,HTTP/1.1 也存在问题。我们选择拆分工作并首先“修复” HTTP/1.1,从而形成现在的 HTTP/2。完成后,工作可以开始替代 TCP,现在是 QUIC。最初,我们希望能够直接在 QUIC 之上运行 HTTP/2,但实际上这会使实现效率太低(主要是由于功能重复)。相反,HTTP/2 在几个关键领域进行了调整,使其与 QUIC 兼容。这个经过调整的版本最终被命名为 HTTP/3(而不是 HTTP/2-over-QUIC),主要是出于营销原因和清晰度。因此,HTTP/1.1 和 HTTP/2 之间的差异比 HTTP/2 和 HTTP/3 之间的差异要大得多。这里的关键要点是,我们需要的并不是真正的 HTTP/3,而是“TCP/2”,并且我们在此过程中“免费”获得了 HTTP/3。我们对 HTTP/3 的主要功能(更快的连接设置、更少的 HoL 阻塞、连接迁移等)感到兴奋,实际上都来自 QUIC。

你可能想知道为什么这很重要?谁在乎这些特性是在 HTTP/3 还是 QUIC 中?我觉得这很重要,因为 QUIC 是一种通用传输协议,与 TCP 非常相似,除了 HTTP 和网页加载之外,它还可以并且将用于许多用例。例如,DNS、SSH、SMB、RTP 等都可以在 QUIC 上运行。因此,让我们更深入地研究 QUIC,因为我读到的关于 HTTP/3 的大部分误解都来自于此。您可能听说过的一件事是,QUIC 运行在另一种协议之上,称为用户数据报协议 (UDP)。这是真的,但不是因为许多人声称的(性能)原因。理想情况下,QUIC 应该是一个完全独立的新传输协议,直接在我上面分享的图像中显示的协议栈中的 IP 之上运行。但是,这样做会导致我们在尝试发展 TCP 时遇到的相同问题:首先必须更新 Internet 上的所有设备才能识别和允许 QUIC。幸运的是,我们可以在 Internet 上另一个广泛支持的传输层协议:UDP 之上构建 QUIC。你知道吗? UDP 是最简单的传输协议。除了所谓的端口号(例如,HTTP 使用端口 80,HTTPS 使用 443 端口,DNS 使用端口 53),它真的不提供任何功能。它不通过握手建立连接,也不可靠:如果 UDP 数据包丢失,则不会自动重新传输。因此,UDP 的“尽力而为”方法意味着它的性能与您所能获得的一样好:无需等待握手,也没有 HoL 阻塞。在实践中,UDP 主要用于以高速率更新的实时流量,因此几乎不会丢失数据包,因为丢失的数据无论如何都会很快过时(示例包括实时视频会议和游戏)。它对于需要低预延迟的情况也很有用;例如,DNS 域名查找确实应该只需要一次往返即可完成。许多消息来源声称,由于性能原因,HTTP/3 建立在 UDP 之上。他们说 HTTP/3 更快,因为就像 UDP 一样,它不建立连接,也不等待数据包重传。这些说法是错误的。正如我们在上面所说的,QUIC 使用 UDP,因此 HTTP/3 主要是因为希望它能让它们更容易部署,因为它已经被(几乎)互联网上的所有设备知道并实现了.然后,在 UDP 之上,QUIC 基本上重新实现了几乎所有使 TCP 成为如此强大和流行(但有点慢)协议的功能。 QUIC 是绝对可靠的,它使用对接收到的数据包和重传的确认来确保丢失的数据包仍然到达。 QUIC 仍然会建立连接并进行高度复杂的握手。最后,QUIC 还使用所谓的流量控制和拥塞控制机制,以防止发送方或接收方过载,但这也会使 TCP 比原始 UDP 慢。关键是 QUIC 以比 TCP 更智能、更高效的方式实现了这些功能。它结合了 TCP 数十年的部署经验和最佳实践以及一些核心新功能。我们将在本文后面更深入地讨论这些功能。

这里的关键要点是,天下没有免费的午餐。 HTTP/3 并没有神奇地比 HTTP/2 快,因为我们将 TCP 换成了 UDP。相反,我们重新构想并实现了一个更高级的 TCP 版本,并将其称为 QUIC。因为我们想让 QUIC 更容易部署,所以我们在 UDP 上运行它。那么,QUIC 究竟是如何改进 TCP 的呢?有什么不同? QUIC 中有几个新的具体特性和机会(0-RTT 数据、连接迁移、对丢包和慢速网络的更大弹性),我们将在本系列的下一部分中详细讨论。然而,所有这些新事物基本上归结为四个主要变化: 如前所述,TLS(传输层安全协议)负责保护和加密通过 Internet 发送的数据。当您使用 HTTPS 时,您的明文 HTTP 数据首先由 TLS 加密,然后由 TCP 传输。你知道吗?幸运的是,这里不需要 TLS 的技术细节;您只需要知道加密是使用一些非常高级的数学和非常大的(质数)数字完成的。这些数学参数是在单独的 TLS 特定加密握手期间在客户端和服务器之间协商的。就像 TCP 握手一样,这种协商可能需要一些时间。在旧版本的 TLS(例如,1.2 版及更低版本)中,这通常需要两次网络往返。幸运的是,较新版本的 TLS(1.3 是最新版本)将其减少为一次往返。这主要是因为 TLS 1.3 将可以协商的不同数学算法严格限制为少数(最安全的)。这意味着客户端可以立即猜测服务器将支持哪些,而不必等待显式列表,从而节省了往返行程。在互联网的早期,加密流量在处理方面的成本非常高。此外,也并非所有用例都需要它。从历史上看,TLS 是一个完全独立的协议,可以选择性地在 TCP 之上使用。这就是我们区分 HTTP(无 TLS)和 HTTPS(有 TLS)的原因。随着时间的推移,我们对互联网安全的态度当然已经转变为“默认安全”。因此,虽然理论上 HTTP/2 可以在没有 TLS 的情况下直接通过 TCP 运行(这甚至在 RFC 规范中定义为明文 HTTP/2),但实际上没有(流行的)Web 浏览器支持这种模式。在某种程度上,浏览器供应商有意识地以牺牲性能为代价来获得更高的安全性。鉴于这种向永远在线的 TLS(尤其是网络流量)的明显演变,QUIC 的设计者决定将这一趋势提升到一个新的水平也就不足为奇了。他们并没有简单地为 HTTP/3 定义明文模式,而是选择将加密深深植入 QUIC 本身。虽然 QUIC 的第一个 Google 特定版本为此使用了自定义设置,但标准化的 QUIC 直接使用现有的 TLS 1.3 本身。

为此,它打破了协议栈中协议之间典型的清晰分离,正如我们在上图中所见。虽然 TLS 1.3 仍然可以在 TCP 之上独立运行,但 QUIC 反而封装了 TLS 1.3。换句话说,没有 TLS 就无法使用 QUIC; QUIC(以及扩展为 HTTP/3)始终是完全加密的。此外,QUIC 还加密了几乎所有的数据包头字段; QUIC 中的中介无法再读取传输层信息(例如数据包编号,这些数据从未为 TCP 加密)(甚至某些数据包标头标志已加密)。对于所有这些,QUIC 首先或多或少地使用 TLS 1.3 握手来建立数学加密参数,就像使用 TCP 一样。然而,在此之后,QUIC 接管并加密数据包本身,而使用 TLS-over-TCP,TLS 进行自己的加密。这种看似微小的差异代表了在越来越低的协议层强制执行的永远在线加密的基本概念变化。 QUIC 对其用户更安全。无法运行明文 QUIC,因此攻击者和窃听者监听的选项也更少。 (最近的研究表明 HTTP/2 的明文选项有多么危险。)QUIC 的连接设置更快。对于 TLS-over-TCP,两种协议都需要自己单独的握手,而 QUIC 将传输和加密握手合二为一,节省了往返(见上图)。我们将在第 2 部分(即将推出!)中更详细地讨论这个问题。 QUIC 可以更容易地进化。因为它是完全加密的,网络中的中间件不能再像使用 TCP 那样观察和解释其内部工作原理。因此,它们也不会因为更新失败而在较新版本的 QUIC 中(意外地)中断。如果我们想在未来为 QUIC 添加新功能,我们“只需要”更新终端设备,而不是所有的中间件。许多网络会犹豫是否允许 QUIC。公司可能希望在他们的防火墙上阻止它,因为检测不需要的流量变得更加困难。 ISP 和中间网络可能会阻止它,因为不再容易获得诸如平均延迟和丢包率之类的指标,从而使检测和诊断问题变得更加困难。这一切都意味着 QUIC 可能永远不会普遍可用,我们将在第 3 部分(即将推出!)中讨论更多。 QUIC 具有更高的加密开销。 QUIC 使用 TLS 加密每个单独的数据包,而 TLS-over-TCP 可以同时加密多个数据包。对于高吞吐量场景,这可能会使 QUIC 变慢(我们将在第 2 部分中看到(即将推出!))。

QUIC 使网络更加集中。我经常遇到的抱怨是,“谷歌正在推动 QUIC,因为它让他们可以完全访问数据,同时不与其他人共享任何数据”。我大多不同意这一点。首先,与 TLS-over-TCP 相比,QUIC 不会向外部观察者隐藏更多(或更少!)用户级信息(例如,您正在访问哪些 URL)(QUIC 保持现状)。其次,虽然谷歌发起了 QUIC 项目,但我们今天讨论的最终协议是由互联网工程任务组 (IETF) 中更广泛的团队设计的。 IETF 的 QUIC 在技术上与谷歌的 QUIC 非常不同。尽管如此,IETF 的成员大多来自谷歌和 Facebook 等大公司以及 Cloudflare 和 Fastly 等 CDN,这是事实。由于 QUIC 的复杂性,主要是那些拥有正确和高效部署所需专业知识的公司,例如,实践中的 HTTP/3。这可能会导致这些公司更加集中,这是一个真正的问题。个人笔记:这就是我写这些类型的文章并进行大量技术演讲的原因之一:确保更多人理解......