QUIC 快速 UDP 互联网连接协议

QUIC 快速 UDP 互联网连接协议

KSkun 2020/6/12

2013 年,Google 提出了快速 UDP 互联网连接协议(QUIC),实现了一种可以替代 TLS/TCP 模式的传输协议,并且将其应用在 Google 搜索、YouTuBe、Chrome 浏览器等产品上。在这些产品上收集到的数据表明,QUIC 相较于传统的 TLS/TCP 模式具有较大的性能优势。2015 年,QUIC 被提交至 IETF,随后在 2018 年被确定为 HTTP/3 标准中的内容,正式纳入 HTTP 家族。

为什么 Google 要开发 QUIC?为什么选择了 UDP?QUIC 如何优化性能?QUIC 效果如何?接下来,我们将一一解答这些问题,并阐释 QUIC 的基本内容。

现在的 HTTPS 如何工作?

要了解 QUIC 的开发背景,首先需要知道今天的 HTTP 以什么形式工作。因此,我们将介绍 HTTPS 协议栈及其存在的问题,从而引出 QUIC 的优化。

传统 HTTPS 协议栈

传统的 HTTPS 协议栈包含以下三部分:

  • 应用层:HTTP/2,提供 HTTP 应用功能,由客户端(浏览器)在用户空间实现;
  • 安全层:TLS,提供安全的传输链路,由客户端(浏览器)在用户空间实现;
  • 传输层:TCP,提供可靠传输方式,由操作系统在内核中实现。

在最近的几年中,上述几种协议均进行了许多改进,例如:

  • HTTP/2 解决了 HTTP/1.1 的性能问题,对 HTTP 头进行压缩,引入了 Server Push(服务器主动推送流,而不是等客户端发送请求后再响应,减少了网页加载多次请求资源的性能问题),在单个 TCP 连接里实现多路复用请求等;
  • TLS 1.3 也优化了 TLS 1.2 存在的一些问题,包括废弃了过时的加密算法,支持 0-RTT 传输(优化握手时间)等;
  • TCP 中,CUBIC、BBR 等优秀的拥塞控制算法提高了网络资源的利用率,优化了传输效率。

容易发现,HTTP 与 TLS 都进行了较大规模的改动,而 TCP 并没有对其协议本身进行修订,这一现象是存在原因的。

「僵化」与「耦合」

在 QUIC 的论文中,Google 使用了僵化(ossification)一词来形容现在的互联网中间设备。这是因为,许多设备供应商在设备中预置了一些规则,如防火墙会禁止已有规则之外的传输,NAT 会以一定规则重写传输头等。如果改动 TCP 协议,这些设备必须更新规则才能支持变动,设备更新的不及时则会影响修订的推进。

此外,由于 TCP 通常实现在操作系统内核中,这种 TCP 与操作系统的耦合(coupling)导致如果修改 TCP 协议,则必须升级操作系统内核,而内核的改动与升级的下发的时间周期很长,也影响了修订的推进。

协议本身存在的问题

由于 TLS 是在 TCP 建立连接后再进行握手,除了 TCP 建立连接时的 1-RTT 时间开销,TLS 还增加了一个 2-RTT 的握手环节。Web 连接大多都是短暂的连接,频繁进行不必要的重复握手对短传输性能存在影响。

QUIC8 - QUIC 快速 UDP 互联网连接协议
图 1:TLS 握手的流程

在 HTTP/1.1 和 HTTP/2 中,协议限制了最大 TCP 连接数量,而 HTTP/2 使用了多路复用同一 TCP 连接的技术,这带来了新的问题:行头阻塞(head-of-line blocking)。当复用同一 TCP 连接时,由于要求响应按顺序传输,第一个响应丢包重传时,会阻塞后面耗时较短的响应及时传输。

QUIC9 - QUIC 快速 UDP 互联网连接协议
图 2:行头阻塞的示意图

QUIC 是什么?

广泛使用的 TLS/TCP 仍存在很大的改进空间,但由于种种困难无法推行。为了彻底解决 TCP 带来的麻烦,Google 选择了 UDP,继而创造出了本文的主角——QUIC。

QUIC(Quick UDP Internet Connection,快速 UDP 互联网连接协议)是一种以 UDP 为底层传输协议,支持加密、多路复用,工作在用户空间的的低延迟传输协议。

QUIC1 1024x512 - QUIC 快速 UDP 互联网连接协议
图 3:QUIC 相对于传统 HTTPS 协议栈的地位

如图所示,QUIC 取代了传统 HTTPS 协议栈中部分 TCP、TLS 与部分 HTTP/2 的位置,向下使用 UDP 进行传输,向上提供 HTTP 接口。QUIC 具有与 HTTPS 相同的接口与安全特性,且低延迟、高效率、能够进行快速的迭代更新,有效解决了上面所提到了诸多问题。

接下来,我们通过 Google 提供的实验数据了解 QUIC 优化传输效率的效果。Google 通过握手延迟、搜索延迟(在 Google 搜索引擎上实验)、视频延迟、重缓冲率(视频暂停缓冲的时间占总时间的比例,皆在 YouTuBe 上实验)来考察 QUIC 对优化不同应用场景性能的效果。

QUIC2 1024x384 - QUIC 快速 UDP 互联网连接协议
图 4:在不同 RTT 链路情况中,各种协议的握手延迟
QUIC3 1024x384 - QUIC 快速 UDP 互联网连接协议
图 5:在不同 RTT 链路情况中,各种协议的搜索延迟
QUIC4 1024x384 - QUIC 快速 UDP 互联网连接协议
图 6:在不同 RTT 链路情况中,各种协议的视频延迟
QUIC5 1024x384 - QUIC 快速 UDP 互联网连接协议
图 7:在不同 RTT 链路情况中,各种协议的视频重缓冲率

可以发现,在各种情况下,QUIC 的性能不同程度优于 TCP/TLS 的表现。

QUIC 如何工作?

QUIC 是如何实现优秀的性能的?要解答这个问题,就必须理解 QUIC 的工作机制,并将其与 TLS/TCP 模式对比,从而体现 QUIC 的优势。接下来,我们将简要介绍 QUIC 的几个核心机制。

建立连接

QUIC6 - QUIC 快速 UDP 互联网连接协议
图 8:三种不同的 QUIC 的握手情形

上图描述了三种 QUIC 的握手情形,接下来分情况研究这三种情形。

初始握手:第一次握手之前,客户端并不知道任何有关服务器的信息,因此客户端发送一个不完整的客户端问候(CHLO)信息,服务器回应一个拒绝(REJ)信息。服务器回应的 REJ 中包含了服务器的配置细节(包含服务器长期公钥)、证书链、签名、源地址令牌。源地址令牌包含了客户端的公共 IP 地址与时间戳,客户端在接下来的信息中包含这一令牌来代表自己的身份。客户端通过签名来验证服务器发送的 REJ 信息,并将使用这些信息发送完整的 CHLO(包含客户端的临时公钥)。

最终握手(或重复握手):初始握手使客户端获得了与服务器建立连接的所有细节,通过发送完整的 CHLO 信息与服务器成功建立连接。此时客户端使用的密钥为初始密钥,包含客户端的临时私钥和服务器的长期公钥。同时,为了达到 0-RTT 的握手延迟,客户端会同时开始使用已知信息发送数据,而非等待服务器回应后再发送。

如果握手成功,服务器回复一个服务器问候(SHLO)信息,这个信息使用初始密钥加密后发送,包含了服务器的临时公钥。客户端收到 SHLO 后,将切换使用服务器的临时公钥加密。这样一来,除了客户端最初的数据使用初始密钥加密,其他数据都使用临时密钥加密,实现了数据的前向安全性(即使服务器长期密钥泄露,也无法使之前的数据加密失效)。

客户端会缓存服务器配置与源地址令牌,在下一次连接同一服务器时,直接以缓存的配置与服务器进行握手,实现 0-RTT 握手。

客户端缓存的配置或令牌有可能过期,此时服务器会直接发送 REJ 来拒绝握手,并附带最新的信息。客户端从 REJ 中提取最新信息,更新缓存后即可再发起握手。

版本协商:客户端会首先提议一个版本,并以该版本传输数据。如果服务器不支持客户端提议的版本,则发回一个强制版本协商数据,包含了服务器支持的所有版本。如果服务器的版本总是高于客户端版本,则版本协商是 0-RTT 的,因此鼓励服务器及时更新 QUIC 版本。为了防止降级攻击,生成密钥的时候将把客户端与服务器发送的版本信息也纳入参数之中,避免信息被修改。

多路复用

为了避免产生行头阻塞的问题,QUIC 支持在同一连接中同时传输多个流,丢包只影响到涉及到的流,而其他流仍然能正常传输而不被阻塞。

QUIC 流是可靠的双向字节流,单个流最高可以传输 264 字节数据。流非常轻量,因此发送少量数据时也可以使用新流。流通过流 ID 进行标识,为了避免 ID 冲突,客户端发起的流 ID 为奇数,服务器为偶数。流可以在发送数据时隐式发起,通过在最后一个流帧中设置 FIN 标记关闭。如果流不再需要,可以取消流而不断开 QUIC 连接。

QUIC 数据包的格式如下,一个数据包包括公共头与多个流帧,每个流帧具有自己的头与数据,数据包可以承载来自不同流的流帧。

QUIC7 1024x673 - QUIC 快速 UDP 互联网连接协议
图 9:QUIC 数据包的结构

QUIC 发送数据的速率是受限的,因此必须决定如何在多个流之间分配速率。在论文实现中,QUIC 使用 HTTP/2 流优先级来分配速率。

身份验证和加密

QUIC 数据包中未加密的部分(包头等)对路由或解密数据都是必要的。Flags 编码了连接 ID 与包编号的长度;连接 ID 可以被负载均衡使用来分配服务器或被服务器用于识别连接状态;版本号和多样化 nonce 只出现在较早的包中,服务器生成 nonce 为客户端生成密钥增加熵值。两方都以包编号作为每个包的 nonce,用于进行加密与解密。

在未加密的握手包(如版本协商信息)中包含的信息都被用于生成最终密钥,篡改这些包会导致生成的密钥不一致,从而导致连接失败。当服务器中不存在连接状态时,会发出重置包,通常路由更改或服务器重启会造成这种情况。由于服务器不知道连接的密钥,重置包只能不加密且不经验证。

丢包恢复

由于 TCP 重传时,ACK 包的编号与原始编号相同,ACK 接受者无法判断确认的是重传还是原始包,必须等待 1-RTT 才能确认这一信息。每个 QUIC 包都有不同的编号,重传包也是一样,因此不需要区分是原始还是重传包。在 QUIC 包中,包编号表示时间顺序,而流帧中的偏移量表示数据的顺序,这些信息可以更准确地检测丢包。

QUIC ACK 包含了接受数据与发送 ACK 之间的延迟,包含这一信息有助于更准确地估计 RTT,为 BBR 等拥塞控制算法提供帮助。

流量控制

QUIC 同时限制了单个流可以消耗的缓冲区大小与一个连接的缓冲区大小,避免了流与连接之间的行头阻塞。

QUIC 接收方会首先发送每个流中的缓冲窗口大小,并定期在流中发送窗口更新帧来扩大窗口大小限制。连接级窗口也用相似的方法控制。此外,实现还采用了类似 TCP 中的调整。

拥塞控制

QUIC 不依赖于某一特定拥塞控制算法,而是提供控制接口,支持多种算法。

连接迁移

QUIC 连接由一个 64 位的连接 ID 标识,该 ID 可以保存连接的状态。当客户端连接发生迁移时,可以通过连接 ID 来迁移连接的状态,避免 NAT 更改丢失状态的问题。

QUIC 发现

客户端并不知道服务器是否支持 QUIC,因此客户端第一次发送 HTTP 请求时先使用 TLS/TCP,而服务器在响应中附带一个 Alt-Svc 头来说明支持 QUIC。

在后序请求中,客户端争用 QUIC 和 TLS/TCP,但尝试 QUIC 略微超前 TLS/TCP。如果 QUIC 无法连接,则客户端之后使用 TLS/TCP。

QUIC 的局限性

尽管在实验中表现优秀,QUIC 仍然具有其局限性。Google 在其论文中提到了以下几点问题:

超前连接:应用程序通过提前进行握手来避免握手造成的延迟,这种情况下 QUIC 的 0-RTT 握手并不具有太大的优势。

高带宽、低延迟、低丢包率的网络:在这样的网络中使用 QUIC 几乎没有优势,反而有可能带来性能损失。

移动设备:QUIC 在移动设备中优势也不大,通常是因为移动设备应用针对其网络特点进行了优化。而 QUIC 需要较多的 CPU 性能,在移动设备上表现并不优。

UDP 限制:部分网络会限制或禁用 UDP 流量,QUIC 无法建立连接。

网络中间设备的僵化:QUIC 是一种快速迭代的协议,中间设备有时会附带对 QUIC 连接的限制规则,当 QUIC 更新时设备规则并未及时更新,从而影响到 QUIC 的正常连接。

CPU 占用较高:QUIC 的 CPU 使用率约是 TLS/TCP 的 3.5 倍。Google 通过优化加密算法等方式降低 CPU 使用率至 TLS/TCP 的 2 倍,但仍然较高。

结语

Google 对互联网技术发展做出的贡献巨大,相继创造了 BBR 拥塞控制算法、QUIC 协议等更高效的互联网技术,并推动它们成为现代互联网基础设施的一部分。

截至目前,QUIC 还未得到大规模的部署与使用。我们期待着 QUIC 能够在未来的互联网中充分发挥其性能优势,在互联网中创造更多可能性;同时也期待着更多优秀的互联网技术被创造、被接纳、被广泛使用,让世界更加触手可及。

参考资料

本文引用的图片部分来源于论文或互联网,引用来源均列入参考资料列表中,感谢原作者的分享。



发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据