你可能读过一些博客文章或听过关于这个主题的会议讲座,认为自己知道答案。你可能听说过这样的话。"当有数据包丢失时,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开发人员。然而,它确实包含了足够的技术细节和许多外部资源的链接,对更高级的读者来说也是有意义的。
为什么我们需要HTTP/3?
我经常遇到的一个问题是,"为什么在2015年才标准化的HTTP/2之后,我们这么快就需要HTTP/3?"这的确很奇怪,直到你意识到,我们一开始并不真的需要一个新的HTTP版本,而是需要升级底层的_传输控制协议_(TCP)。
TCP是为HTTP等其他协议提供可靠性和有序交付等关键服务的主要协议。它也是我们能够继续使用有许多并发用户的互联网的原因之一,因为它巧妙地将每个用户的带宽使用限制在他们的公平份额。
你知道吗?
当使用HTTP(S)时,你实际上是在同时使用HTTP以外的几个协议。这个 "堆栈 "中的每个协议都有自己的特点和职责(见下图)。例如,当HTTP处理URL和数据解释时,传输层安全(TLS)通过加密来确保安全,TCP通过重传丢失的数据包来实现可靠的数据传输,而互联网协议(IP)将数据包从一个终端传送到另一个终端,并在中间的不同设备(中间箱)之间进行路由。
这种将协议 "分层 "在彼此之上的做法是为了便于重复使用其功能。高层协议(如HTTP)不必重新实现复杂的功能(如加密),因为低层协议(如TLS)已经为他们做了这些。再比如,互联网上的大多数应用都在内部使用TCP,以确保其所有的数据都能完整地传输。由于这个原因,TCP是互联网上最广泛使用和部署的协议之一。
几十年来,TCP一直是网络的基石,但在2000年代末,它开始显示其年龄。它的预期替代者,一个名为QUIC的新传输协议,在一些关键方面与TCP有足够的不同,以至于直接在它上面运行HTTP/2将非常困难。因此,HTTP/3本身是对HTTP/2的一个相对较小的调整,以使其与新的QUIC协议兼容,其中包括大多数人们兴奋的新功能。
之所以需要QUIC,是因为自互联网早期就存在的TCP并没有真正考虑到最大效率。例如,TCP需要一个 "握手"来建立一个新的连接。这样做是为了确保客户和服务器都存在,而且他们愿意并能够交换数据。然而,它也需要一个完整的网络往返,才能在连接上做其他事情。如果客户端和服务器在地理上相距甚远,每个往返时间(RTT)可能需要超过100毫秒,会产生明显的延迟。
第二个例子,TCP将其传输的所有数据视为一个单一的 "文件 "或字节流,即使我们实际上同时使用它来传输几个文件(例如,在下载一个由许多资源组成的网页时)。在实践中,这意味着如果包含单个文件数据的TCP数据包丢失,那么所有其他文件也会被延迟,直到这些数据包被恢复。
这就是所谓的_线头(HoL)阻塞_。虽然这些低效率在实践中是可以管理的(否则,我们就不会使用TCP超过30年了),但它们确实以明显的方式影响了更高级别的协议,如HTTP。
随着时间的推移,我们试图对TCP进行发展和升级,以改善其中的一些问题,甚至引入新的性能特征。例如,TCP快速开放通过允许高层协议从一开始就发送数据来摆脱握手的开销。另一项努力被称为MultiPath TCP。这里的想法是,你的手机通常有Wi-Fi和(4G)蜂窝网络连接,那么为什么不同时使用它们以获得额外的吞吐量和稳健性?
实现这些TCP扩展并不十分困难。然而,在互联网规模上实际部署它们是极具挑战性的。因为TCP是如此的流行,几乎每个连接的设备都有自己的协议实现。如果这些实现太老,缺乏更新,或者有错误,那么这些扩展就不能实际使用。换句话说,所有的实现都需要知道这个扩展,这样它才会有用。
如果我们只讨论终端用户设备(如你的电脑或网络服务器),这就不是什么问题,因为这些设备可以相对容易地手动更新。然而,许多其他设备坐在客户端和服务器之间,它们也有自己的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。
什么是QUIC?
你可能想知道为什么这很重要?谁在乎这些功能是在HTTP/3还是QUIC中?我觉得这很重要,因为QUIC是一个通用的传输协议,就像TCP一样,除了HTTP和网页加载之外,还可以并将被用于许多用例。例如,DNS、SSH、SMB、RTP等都可以通过QUIC运行。因此,让我们更深入地了解一下QUIC,因为我读到的关于HTTP/3的大多数误解都是来自于此。
你可能听说过的一件事是,QUIC运行在另一个协议之上,称为_用户数据报协议_(UDP)。这是真的,但不是因为许多人声称的(性能)原因。理想情况下,QUIC将是一个完全独立的新传输协议,直接运行在我上面分享的图片中所示的协议栈的IP之上。
然而,这样做会导致我们在试图发展TCP时遇到的同样问题:互联网上的所有设备都必须首先更新,以便识别和允许QUIC。幸运的是,我们可以在互联网上其他广泛支持的传输层协议之上建立QUIC。UDP。
你知道吗?
UDP是最简陋的传输协议。除了所谓的端口号外,它真的不提供任何功能(例如,HTTP使用80端口,HTTPS在443上,而DNS采用53端口)。它不通过握手来建立连接,也不可靠。如果一个UDP数据包丢失,它不会自动重传。因此,UDP的 "尽力而为 "方法意味着它的性能和你能得到的差不多:不需要等待握手,也没有HoL阻塞。在实践中,UDP主要用于高速更新的实时流量,因此很少受到丢包的影响,因为丢失的数据很快就会过时(例子包括实时视频会议和游戏)。它也适用于需要低前期延迟的情况;例如,DNS域名查询真的应该只需要一次往返就能完成。
许多消息来源声称,HTTP/3是建立在UDP之上的,因为性能问题。他们说,HTTP/3更快,因为就像UDP一样,它不需要建立连接,也不需要等待数据包重传。**这些说法是错误的。**正如我们上面所说的,UDP被QUIC使用,因此,HTTP/3主要是因为希望它能使它们更容易部署,因为它已经被互联网上(几乎)所有的设备知道并实施。
在UDP的基础上,QUIC基本上重新实现了几乎所有使TCP成为如此强大和流行(但有点慢)的协议的功能。QUIC是绝对可靠的,对收到的数据包使用确认,并进行重传以确保丢失的数据包仍然到达。QUIC也仍然建立了一个连接,并有一个高度复杂的握手。
最后,QUIC还使用了所谓的流量控制和拥堵控制机制,防止发送方使网络或接收方过载,但这也使TCP比你用原始UDP做的要慢。最关键的是,QUIC以一种比TCP更智能、更高性能的方式实现这些功能。它将几十年的部署经验和TCP的最佳实践与一些核心的新功能结合起来。我们将在本文后面更深入地讨论这些功能。
启示
这里的关键收获是,没有免费的午餐这回事。HTTP/3并不因为我们把TCP换成了UDP就神奇地比HTTP/2快。相反,我们已经重新设想并实施了一个更先进的TCP版本,并称之为QUIC。因为我们想让QUIC更容易部署,所以我们通过UDP运行它。
大的变化
那么,QUIC究竟是如何改进TCP的?有什么不同呢?QUIC有几个新的具体功能和机会(0-RTT数据、连接迁移、对丢包和慢速网络的更大弹性),我们将在该系列的下一部分中详细讨论。然而,所有这些新东西基本上可以归结为四个主要变化。
- QUIC与TLS深度整合。
- QUIC支持多个_独立的_字节流。
- QUIC使用连接ID。
- QUIC使用框架。
让我们仔细看看这些要点。
没有TLS就没有QUIC
如前所述,TLS(传输层安全协议)负责在互联网上发送的数据的安全和加密。当你使用HTTPS时,你的明文HTTP数据首先被TLS加密,然后由TCP传输。
你知道吗?
TLS的技术细节,幸运的是,在这里并不是真的需要;你只需要知道,加密是通过一些相当先进的数学和非常大的(素数)来完成的。这些数学参数是在单独的TLS特定的加密握手期间在客户和服务器之间协商的。就像TCP握手一样,这种协商可能需要一些时间。 在旧版本的TLS中(例如,1.2和更低的版本),这通常需要两次网络往返旅行。幸运的是,较新版本的TLS(1.3是最新版本)将这一过程减少到只需一次往返。这主要是因为TLS 1.3严格限制了可以协商的不同数学算法,只有少数几种(最安全的算法)。这意味着客户端可以立即猜测服务器将支持哪些算法,而不必等待一个明确的列表,从而节省了一次往返的时间。
在互联网的早期,加密流量的处理成本相当高。此外,它也不被认为对所有的使用情况都有必要。因此,在历史上,TLS一直是一个完全独立的协议,可以_选择_在TCP之上使用。这就是为什么我们有HTTP(无TLS)和HTTPS(有TLS)的区别。
随着时间的推移,我们对互联网安全的态度当然已经转变为 "默认安全"。因此,虽然理论上HTTP/2可以直接通过TCP运行而不需要TLS(这甚至在RFC规范中被定义为明文HTTP/2),但实际上没有(流行的)网络浏览器支持这种模式。在某种程度上,浏览器供应商以牺牲性能为代价,有意识地权衡了更多的安全性。
考虑到这种向始终在线的TLS的明显演变(特别是对于网络流量),QUIC的设计者决定将这一趋势提升到新的水平,这并不奇怪。他们没有简单地不为HTTP/3定义一个明文模式,而是选择将加密深深地植入QUIC本身。虽然QUIC的第一个谷歌专用版本为此使用了一个自定义设置,但标准化的QUIC直接使用现有的TLS 1.3本身。
为此,它有点打破了协议栈中典型的协议之间的清洁分离,正如我们在前面的图片中看到的那样。虽然TLS 1.3仍然可以在TCP之上独立运行,但QUIC反而有点像对TLS 1.3的封装。换句话说,没有TLS就没有办法使用QUIC;QUIC(以及延伸的HTTP/3)总是完全加密的。此外,QUIC还对其几乎所有的数据包头字段进行了加密;传输层信息(如数据包号码,这在TCP中从未被加密)在QUIC中不再能被中间人读取(甚至一些数据包头的标志也被加密)。
对于这一切,QUIC首先使用TLS 1.3握手,或多或少像使用TCP那样建立数学加密参数。然而,在这之后,QUIC接管并自行加密数据包,而在TLS-over-TCP中,TLS进行自己的加密。这个看似微小的差异代表了一个基本的概念性变化,即在更低的协议层上执行始终在线的加密。
这种方法为QUIC提供了几个好处。
- QUIC对其用户来说更安全。
,没有办法运行明文的QUIC,所以攻击者和窃听者可以监听的选项也更少。(最近的研究表明,HTTP/2的明文选项是多么危险)。) - QUIC的连接建立速度更快。
对于TLS-over-TCP,两个协议都需要各自独立的握手,而QUIC则将传输和加密握手合二为一,节省了一次往返的时间(见上图)。我们将在第二部分更详细地讨论这个问题_(即将到来!_)。 - QUIC可以更容易地发展。
因为它是完全加密的,网络中的中间盒不能再像TCP那样观察和解释其内部运作。因此,它们也不能再因为没有更新而在较新版本的QUIC中破坏(意外地)。如果我们想在未来为QUIC增加新的功能,我们 "只 "需要更新终端设备,而不是所有的中间箱。
然而,除了这些好处之外,广泛加密也有一些潜在的缺点。
- 许多网络将犹豫是否允许QUIC。
公司可能想在其防火墙上阻止它,因为检测不需要的流量变得更加困难。ISP和中间网络可能会阻止它,因为平均延迟和丢包率等指标不再容易获得,使得检测和诊断问题更加困难。这一切意味着QUIC可能永远不会被普遍使用,我们将在第三部分_(即将到来!_)中进一步讨论。 - QUIC有更高的加密开销。
QUIC用TLS对每个单独的数据包进行加密,而TLS-over-TCP可以同时对几个数据包进行加密。这有可能使QUIC在高吞吐量的情况下变得更慢(我们将在第二部分看到_(即将到来!_))。 - QUIC使网络更加集中。
我经常遇到的一个抱怨是,"QUIC是由谷歌推动的,因为它使他们能够完全访问数据,而不与其他人分享任何数据"。我大多不同意这种说法。首先,与TLS-over-TCP相比,QUIC并没有向外部观察者隐藏更多(或更少!)的用户级信息(例如,你正在访问哪些URL)(QUIC保持现状)。
其次,虽然谷歌发起了QUIC项目,但我们今天讨论的最终协议是由互联网工程任务组(IETF)中一个更广泛的团队设计的。IETF的QUIC在技术上与谷歌的QUIC非常不同。不过,IETF中的人确实大多来自谷歌和Facebook等大公司以及Cloudflare和Fastly等CDN。由于QUIC的复杂性,主要是这些公司拥有必要的技术知识来正确和有效地部署,例如在实践中部署HTTP/3。这可能会导致这些公司更加集中化,这_是一个真正的问题。
就个人而言:
这是我写这类文章和做大量技术讲座
的
原因之一:确保更多的人了解协议的细节,并能独立于这些大公司使用它们。
启示
这里的关键收获是,QUIC默认是深度加密的。这不仅提高了其安全性和隐私特性,而且有助于其可部署性和可进化性。它使协议的运行变得有点沉重,但作为回报,允许其他优化,例如更快的连接建立。
QUIC知道多字节流的情况
TCP和QUIC之间的第二个重大区别是有点技术性的,我们将在第二部分更详细地探讨其影响_(即将到来!_)。不过现在,我们可以从高层次的角度来理解其主要方面。
你知道吗?
首先考虑一下,即使是一个简单的网页也是由许多独立的文件和资源组成的。有HTML、CSS、JavaScript、图片等等。这些文件中的每一个都可以被看作是一个简单的 "二进制blob"--由零和一组成的集合,被浏览器以某种方式解释。 当在网络上发送这些文件时,我们并不是一次性将它们全部传输。相反,它们被细分为更小的块(通常,每个约1400字节),并在单独的数据包中发送。因此,我们可以把每个资源看作是一个单独的 "字节流",因为数据是随着时间的推移被下载或 "流 "的。
对于HTTP/1.1来说,资源加载过程非常简单,因为每个文件都有自己的TCP连接并被完全下载。例如,如果我们有文件A、B和C,我们将有三个TCP连接。第一个会看到一个AAAA的字节流,第二个是BBBB,第三个是CCCC(每个字母的重复都是一个TCP包)。这样做是可行的,但也是非常低效的,因为每个新连接都有一些开销。
在实践中,浏览器对可使用的并发连接数进行了限制(因此也限制了可并行下载的文件数)--通常情况下,每次页面负载在6到30个之间。一旦前一个文件完全传输完毕,连接就会被重新使用,以下载一个新的文件。这些限制最终开始阻碍了现代网页的性能,这些网页经常加载超过30个资源。
改善这种情况是HTTP/2的主要目标之一。该协议通过不再为每个文件打开一个新的TCP连接,而是通过一个TCP连接下载不同的资源。这是通过**"复用 "不同的字节流**来实现的。这是一种华丽的说法,即我们在传输不同文件的数据时将其混合。对于我们的三个例子文件,我们将得到一个单一的TCP连接,传入的数据可能看起来像AABBCCAABBCC(尽管许多其他的排序方案是可能的)。这看起来很简单,而且确实运行得很好,使HTTP/2通常与HTTP/1.1一样快或更快一些,但开销要少得多。
让我们仔细看看其中的差别。
然而,在TCP方面有一个问题。你看,因为TCP是一个更古老的协议,而且不是为加载网页而做的,它不知道A、B或C。在内部,TCP认为它只是在传输一个单一的文件,X,而且它不关心它认为的XXXXXXXXX实际上是HTTP层面的AABBCCAABBCC。在大多数情况下,这并不重要(它实际上使TCP相当灵活!),但当网络上出现例如丢包的情况时,情况就变了。
假设第三个TCP数据包丢失了(包含文件B的第一个数据的那个),但所有其他的数据都被送达。TCP通过在一个新的数据包中重新传输丢失数据的新副本来处理这一损失。然而,这种重传可能需要一段时间才能到达(至少一个RTT)。你可能认为这不是一个大问题,因为我们看到资源A和C没有丢失。因此,我们可以在等待B的丢失数据时开始处理它们,对吗?
不幸的是,情况并非如此,因为重传逻辑发生在TCP层,而TCP并不知道A、B和C的情况。相反,TCP认为单个X文件的一部分已经丢失了,因此它觉得它必须保持X的其余数据不被处理,直到这个洞被填补。换句话说,虽然在HTTP/2层面上,我们知道我们已经可以处理A和C,但TCP不知道这一点,导致事情比他们可能的速度要慢。这种低效率是"线头(HoL)阻塞 "问题的一个例子。
解决传输层的HoL阻塞问题是QUIC的主要目标之一。与TCP不同,QUIC密切意识到它正在复用多个_独立的_字节流。当然,它不知道它正在传输CSS、JavaScript和图像;它只知道这些数据流是独立的。因此,QUIC可以在每条流的基础上执行丢包检测和恢复逻辑。
在上面的方案中,它将只保留流B的数据,与TCP不同,它将尽快把A和C的任何数据传送到HTTP/3层。(在理论上,这可能会导致性能的提高。然而,在实践中,情况要细微得多,我们将在第二部分讨论_(即将到来!_)。
我们可以看到,我们现在有一个TCP和QUIC之间的根本区别。顺便说一下,这也是为什么我们不能在QUIC上运行HTTP/2的主要原因之一。正如我们所说,HTTP/2还包括在一个单一的(TCP)连接上运行多个流的概念。因此,HTTP/2-over-QUIC将有两个不同的、相互竞争的流抽象。
让它们很好地一起工作将是非常复杂和容易出错的;因此,HTTP/2和HTTP/3之间的一个关键区别是,后者删除了HTTP流逻辑,而是重新使用QUIC流。正如我们将在第二部分看到的那样_(即将到来!_),虽然这对如何实现服务器推送、头压缩和优先级等功能有其他影响。
经验之谈
这里的关键问题是,TCP从来就不是为了在一个连接上传输多个独立文件而设计的。因为这正是网络浏览所需要的,多年来,这导致了许多低效率。QUIC通过使多个字节流成为传输层的核心概念,并在每个字节流的基础上处理数据包丢失,解决了这个问题。
QUIC支持连接迁移
QUIC的第三个主要改进是连接可以保持更长时间的活力。
你知道吗?
我们在谈论网络协议时经常使用 "连接 "的概念。然而,到底什么是连接?通常情况下,一旦两个端点(例如浏览器或客户端和服务器)之间进行了握手,人们就会谈论TCP连接。这就是为什么UDP经常被说成是 "无连接"(有点误导),因为它不做这种握手。然而,握手并没有什么特别之处:它只是一些具有特定形式的数据包被发送和接收而已。它有几个目标,其中最主要的是确保另一方有东西,而且它愿意并能够与我们交谈。在此值得重申的是,QUIC也进行握手,尽管它在UDP上运行,而UDP本身并没有。
那么,问题来了,这些数据包如何到达正确的目的地?在互联网上,IP地址被用来在两台独特的机器之间路由数据包。然而,仅仅拥有你的手机和服务器的IP是不够的,因为都希望能够在每一端同时运行多个联网程序。
这就是为什么每个单独的连接在两个端点上也被分配一个端口号,以区分连接和它们所属的应用程序。服务器应用程序通常有一个固定的端口号,这取决于它们的功能(例如HTTP(S)的端口80和443,DNS的端口53),而客户端通常为每个连接(半)随机地选择其端口号。
因此,为了定义一个跨机器和应用程序的唯一连接,我们需要这四样东西,即所谓的4元组:客户端IP地址+客户端端口+服务器IP地址+服务器端口。
在TCP中,连接只由4元组来识别。因此,如果这四个参数中只有一个发生变化,连接就会失效,需要重新建立(包括新的握手)。为了理解这一点,想象一下停车场的问题:你目前正在一栋有Wi-Fi的大楼内使用你的智能手机。因此,你在这个Wi-Fi网络上有一个IP地址。
如果你现在搬到外面,你的手机可能会切换到蜂窝式的4G网络。因为这是一个新的网络,它将得到一个全新的IP地址,因为这些是网络特定的。现在,服务器将看到来自客户端IP的TCP数据包,这是它以前没有见过的(当然,这两个端口和服务器IP可以保持不变)。如下图所示。
但是,服务器怎么能知道这些来自新IP的数据包属于 "连接"?它怎么知道这些数据包不属于蜂窝网络中另一个选择相同(随机)客户端端口的_新_连接(这很容易发生)?可悲的是,它无法知道这一点。
因为TCP是在我们还没有梦想到蜂窝网络和智能手机的时候发明的,例如,没有任何机制允许客户端让服务器知道它已经改变了IP。甚至没有办法 "关闭 "连接,因为向旧的4-tuple发送的TCP重置或fin命令甚至不会再到达客户端。因此,在实践中,每一个网络变化都意味着现有的TCP连接不能再被使用。
必须执行新的TCP(可能还有TLS)握手以建立新的连接,而且,根据应用级协议,进程中的操作也需要重新启动。例如,如果你是通过HTTP下载一个大文件,那么该文件可能要从头开始重新请求(例如,如果服务器不支持范围请求)。另一个例子是现场视频会议,在切换网络时,你可能会有短暂的停电。
请注意,还有其他原因导致4元组可能发生变化(例如,NAT重新绑定),我们将在第二部分_(即将到来!_)中详细讨论。
因此,重启TCP连接会产生严重影响(等待新的握手、重启下载、重新建立上下文)。为了解决这个问题,QUIC引入了一个名为**连接标识符(CID)**的新概念。每个连接在4元组的基础上被分配一个数字,在两个端点之间唯一地识别它。
最重要的是,由于这个CID是在QUIC本身的传输层定义的,因此在网络之间移动时它不会改变这在下面的图片中显示。为了实现这一点,CID包括在每个QUIC数据包的前面(就像IP地址和端口也存在于每个数据包中一样)。(这实际上是QUIC数据包头中少数没有加密的内容之一!)。)
有了这种设置,即使4元组中的某项内容发生变化,QUIC服务器和客户也只需要看一下CID就知道这是同一个老的连接,然后他们就可以继续使用它。不需要进行新的握手,而且下载状态可以保持不变。这个功能通常被称为_连接迁移_。从理论上讲,这对性能更好,但是,正如我们将在第二部分讨论的那样_(即将到来!_),当然,这又是一个细微的故事。
在CID方面还有其他挑战需要克服。例如,如果我们真的只使用一个CID,这将使黑客和窃听者非常容易在网络上跟踪一个用户,并推断出他们的(大致)物理位置。为了防止这种隐私恶梦,QUIC在每次使用新网络时都会改变CID。
不过,这可能会使你感到困惑。我刚才不是说CID在不同网络中应该是相同的吗?嗯,那是过度简化了。内部真正发生的情况是,客户和服务器同意一个共同的(随机生成的)CID列表,这些CID都映射到同一个概念上的 "连接"。
例如,他们都知道,CIDs K、C和D在现实中都映射到连接X。因此,虽然客户端可能在Wi-Fi上用K标记数据包,但它可以在4G上切换到使用C。这些共同的列表在QUIC中是完全加密的,所以潜在的攻击者不会知道K和C实际上是X,但客户端和服务器会知道这一点,他们可以保持连接的活力。
它变得更加复杂,因为客户和服务器将有他们自己选择的不同的CID列表(就像他们有不同的端口号)。这主要是为了支持大规模服务器设置中的路由和负载平衡,我们将在第三部分中看到更多细节_(即将到来!_)。
经验之谈
这里的关键启示是,在TCP中,连接是由四个参数定义的,当端点改变网络时,这些参数会改变。因此,这些连接有时需要重新启动,导致一些停机时间。QUIC在混合中增加了另一个参数,称为连接ID。QUIC客户端和服务器都知道哪些连接ID映射到哪些连接,因此对网络变化更加稳健。
QUIC是灵活和可发展的
QUIC的最后一个方面是,它被特别设计为易于演进。这是以几种不同的方式实现的。首先,正如所讨论的,QUIC几乎是完全加密的,这意味着如果我们想部署更新的QUIC版本,我们只需要更新端点(客户端和服务器),而不是所有的中间箱。这仍然需要时间,但通常是几个月的时间,而不是几年。
其次,与TCP不同,QUIC不使用单一的固定包头来发送所有协议元数据。相反,QUIC的包头很短,并在包的有效载荷内使用各种 "帧"(有点像微型的专门包)来传递额外信息。例如,有一个ACK 帧(用于确认)、一个NEW_CONNECTION_ID 帧(帮助建立连接迁移)和一个STREAM 帧(携带数据),如下面的图片所示。
这主要是作为一种优化,因为不是每个数据包都携带所有可能的元数据(因此TCP数据包头通常会浪费很多字节--也见上图)。然而,使用框架的一个非常有用的副作用是,在未来定义新的框架类型作为QUIC的扩展将非常容易。例如,一个非常重要的框架是DATAGRAM ,它允许不可靠的数据通过加密的QUIC连接发送。
第三,QUIC使用一个自定义的TLS扩展来携带所谓的传输参数。这些参数允许客户端和服务器为QUIC连接选择配置。这意味着他们可以协商启用哪些功能(例如,是否允许连接迁移,支持哪些扩展,等等),并为一些机制沟通合理的默认值(例如,支持的最大数据包大小,流量控制限制)。虽然QUIC标准定义了一个很长的列表,但它也允许扩展定义新的功能,再次使协议更加灵活。
最后,虽然QUIC本身不是一个真正的要求,但目前大多数实现是在 "用户空间 "完成的(与TCP相反,后者通常是在 "内核空间 "完成的)。细节将在第二部分讨论_(即将到来!_),但这主要意味着实验和部署QUIC实现的变化和扩展比TCP要容易得多。
启示
虽然QUIC现在已经标准化,但它确实应该被视为QUIC版本1(这在Request For Comments (RFC)中也有明确说明),而且有一个明确的意图,即相当快地创建版本2和更多。除此之外,QUIC允许轻松定义扩展,因此可以实现更多的用例。
总结
让我们总结一下我们在这一部分学到的东西。我们主要谈到了无处不在的TCP协议,以及它是如何在今天的许多挑战还不为人所知的时候设计出来的。当我们试图发展TCP以跟上时代的步伐时,我们发现这在实践中是很困难的,因为几乎每个设备上都有自己的TCP实现,需要更新。
为了在改进TCP的同时绕过这个问题,我们创建了新的QUIC协议(它实际上就是引擎盖下的TCP 2.0)。为了使QUIC更容易部署,它运行在UDP协议之上(大多数网络设备也支持该协议),为了确保它能在未来发展,它默认情况下几乎是完全加密的,并使用了灵活的框架机制。
除此以外,QUIC主要反映了已知的TCP特性,如握手、可靠性和拥塞控制。除了加密和成帧之外,两个主要的变化是对多字节流的认识和对连接ID的引入。然而,这些变化足以阻止我们在QUIC之上直接运行HTTP/2,因此有必要创建HTTP/3(这实际上是HTTP/2-over-QUIC)。
QUIC的新方法为一些性能改进让路,但它们的潜在收益比通常在QUIC和HTTP/3的文章中传达的更细微。现在我们知道了基础知识,我们可以在本系列的下一部分中更深入地讨论这些细微差别。请继续关注!