QUIC协议综述

注明: 本文翻译自The QUIC Transport Protocol: Design and Internet-Scale Deployment.

摘要

    基于我们的经验,QUIC协议是一个加密的、多路复用的、低延时的传输协议,它被设计用于提高HTTPS的传输效率进而使得快速部署和持续进化成为可能。 QUIC已经在Google的成千上万台服务器上部署了,并且已经用于为包括像Chrome和YouTube在内的许多客户端提供服务。 我们估计现在的网络流量的7%是QUIC提供的。我们想要叙述我们开发新的传输协议的动机、设计这个协议的原则和用于对QUIC进行迭代实验的互联网规模化处理、性能的较大提升和我们全面部署QUIC的经验。我们还想分享从部署的过程中学习到的传输协议的设计和互联网生态系统。

1. 简介

    QUIC作为一个新的传输协议,有效的提高了HTTPS的传输效率并且可以快速部署和持续的进化。 QUIC替换了大多数传统的HTTPS协议栈: HTTP/2,TLS 和TCP(如图1)。 QUIC是基于UDP开发的一个用户空间的协议。 在用户空间实现该协议能够像其他应用程序那样快速的部署并且能够在应用程序的更新过程中迭代。使用UDP能够让QUIC的包能够穿过协议栈的中间层。 QUIC是一个加密的传输协议: 数据包都进行了认证和加密,可以防止被修改并且限制通过中间协议层限制协议的僵化。 QUIC通过使用服务端已知的证书来加密握手连接并且通过移除在多层网络协议栈中的握手使得握手连接的延时最小化。QUIC通过使用轻量的数据结构抽象和流的概念解决了队头阻塞的延迟问题。多个流在一个连接中能够多路复用,使得一个包的丢失只会block当前流。

Figure 1: QUIC in the traditional HTTPS stack.

    在服务端侧, 我们的经验来自在Google的前端服务器上部署QUIC, 这些服务器每天处理几十亿次的请求,这些请求来自各种浏览器和移动app上的广泛的服务。在客户端,我们已经在Chrome、YouTube和安卓上的搜索app上部署了QUIC。我们发现平均情况下,QUIC能够减少Google搜索百分之8的延迟,减少移动用户3.6%的延迟,对于YouTube用户,减少rebuffer操作的延迟,桌面用户能够减少18%,移动用户减少15.3%。 如图2所示, QUIC广泛的部署了,当前Google向外提供服务的服务器有大约30%已经部署了QUIC, 整个互联网大约有7%使用了QUIC。

Figure 2: Timeline showing the percentage of Google traffic served over
Figure 3: Increase in secure web traffic to Google’s front-end servers.

    我们在2013年就启动了一个QUIC的早期版本作为实验。 经过对协议多次 的迭代并且跟踪部署该协议超过三年的时间,组成了一个IETF的工作小组去将其标准化。在我们的部署中,QUIC是一个庞大的协议, 但是IETF的标准化工作将会将其模块化为多个组成部分。 除了核心协议的划分和指定工作, IETF的工作将会描述一个HTTP到QUIC的明确映射,并分隔和替换QUIC基于最新的TLS 1.3的加密握手。 这篇文章描述了IETF之前的对QUIC协议的设计和部署工作。 QUIC协议的细节将会随着IETF的工作的细化而改变。 我们希望协议的核心设计和性能表现能够保持原样。

    在这篇文章中, 我们经常穿插关于这个协议的讨论以及在HTTPS协议栈中如何使用还有协议的具体实现。在我们之前的工作经验中,这三点是相辅相成的。 这篇文章试图清晰的反映它们之间的关系。

2. 动机: 为什么是QUIC?

    延时敏感的web服务的使用以及作为应用程序平台的web的快速增长,促进了在降低延时方面的空前巨大的要求。 Web延时在提高用户体验中依然还是一个阻碍, tail latency依然是扩展web平台的一个阻碍。 与此同时,互联网正在快速的从不安全的传输转向安全传输, 这个转变的过程中就增加了延时。举一个通常的例子, 图3展示了Google的安全传输怎样在很短的一段时间内喜剧般的增长,这全都归功于Google的服务采用HTTPS的传输方式。在传输机制方面降低延时的努力通常快速进入了TLS/TCP生态系统的基本限制。

    协议保障: 尽管在TCP的简单服务之下,新的传输协议已经被指定来满足应用程序的进化要求,但是它们没有被广泛部署。 当前的网络架构中的中间层意外的成为了关键的控制点: 防火墙处于安全的因素会倾向于阻止所有异常的访问并且NAT协议会重写传输协议的头部,使得两者都无法在不添加对它们明确支持的情况下允许来自新传输的流量。 任何包中的内容都不会被从端到端的保护, 例如TCP包的头部,对于在协议栈的中间层中进行检查和修改变得很公平。 因此, 由于middleboxes的固化,导致甚至只修改TCP协议也会面临巨大的挑战。 对TCP的修改已经达到了收益递减的时点, 现在简单的协议修改也预计需要十年的时间来进行部署。

    实现保障: 随着互联网的持续进化和各种对各种基础设施的多种攻击(包括传输的部分), 这样就需要能够快速的部署到客户端。TCP协议是在操作系统的内核中实现的。 因此, TCP协议的修改需要升级操作系统。这种耦合限制了TCP修改的更新速率。 操作系统的升级有着系统层面的影响并且升级的流水线和机制都需要非常的小心。 即使移动操作系统的普及率越来越高,升级速度也越来越快,但是相当大的用户数量也会落户几年的时间。服务端操作系统的升级相对要快一个数量级,但是也仍然需要耗费数月的时间, 因为对OS的性能和稳定性的高要求。 这个限制了部署和迭代的速度。

上一段讲了如果修改TCP协议来达到升级协议的目的,那就需要在每次修改协议的时候升级操作系统 )

    握手延迟: TCP和TLS的通用性继续服务于互联网的发展,但是随着对HTTPS延迟要求的增长分层的代价也越来越明显。在发送数据之前,TCP连接至少会导致一个往返的延迟时间, 而TLS则需要再增加两轮的延迟时间。随着时间的推移, 网络的带宽不断增加。 大多数互联网上的连接、大多数网络上的交易,只需要很短的传输过程,它们大多数都受到不必要的握手连接的影响。

    队头阻塞延迟: 为了降低多个TCP连接的延迟和开销, HTTP/1.1推荐限制客户端到服务端的连接数量。 为了进一步的降低交易的延迟, HTTP/2多路复用多个对象并且推荐使用单个到服务器的TCP连接。然而,TCP的字节流抽象阻止了应用程序控制传输过程中的帧,并且对应用程序的帧征收了一个“延迟税”,因为这个应用程序必须等待先前丢失帧的重新传输。

    通常,部署web传输的修改需要更改web服务器和客户端,对于服务端的传输协议栈,常常需要修改协议的中间层。 部署对这三个模块的改动需要协调应用程序开发者,OS供应商,middlebox供应商和网络运营商。 QUIC加密传输头部,并且在UDP之上建立传输的功能,避免对供应商和网络运营商的依赖并且将传输过程中的控制权转移到应用程序之上。

3. QUIC 的设计与实现

    QUIC被设计来达到几个目标, 包括可部署、安全性、减少握手开销和队头阻塞延时。QUIC协议合并了它的加密和握手过程来减小连接建立过程中的往返次数。它通过为每个请求和响应提供单个的stream流,实现了通过复用单个连接来处理所有的请求和响应,以便没有其他响应能够阻断这个响应。通过加密和认证数据包来避免被middleboxes篡改,并且避免协议的僵化。 协议通过一下手段来提高丢包恢复能力,通过使用唯一的数据包编号来避免重传二意性,通过使用明确的ACK信号来实现精确的RTT测量。QUIC协议允许跨越IP地址改变来迁移连接,这是通过使用连接的ID来识别连接而不是IP/端口5元组。QUIC协议提供了流控制来限制在一个慢速的接收者上的流量,并且通过对每一个流使用流控制限制来确保单个流不会消耗接收者所有的buffer。我们的实现提供了一个模块化的拥塞控制接口用于实验各种控制器。 我们的客户端和服务端在不增加延迟的情况下,协商协议的使用。 这个段落指明了QUIC中的设计和实现的细节。我们在这篇文章中不描述这种有线格式的细节,有兴趣的朋友可以去查看IETF的相关文档。

3.1 连接建立

Figure 4: Timeline of QUIC’s initial 1-RTT handshake, a subsequent successful 0-RTT handshake, and a failed 0-RTT handshake.

    QUIC协议依赖于组合加密和传输过程中的握手来 创建一个安全的传输连接。 在成功连接的情况下, 客户端缓存起来原始的信息。 在接下来与相同的服务器建立连接的过程中, 客户端能够在不增加额外RTT的情况下建立一个加密的连接,数据要发送的数据可以在握手的包中捎带着发送过去,而不用等待服务器的回复。QUIC提供一个专用的可靠流(流在下文中定义)来执行加密握手。 本段落总结了QUIC的加密握手的机制,还有QUIC是怎么实现0-RTT的连接建立过程。 如图4所示。

    初始握手: 初始时, 客户端客户端没有服务端的任何信息, 在握手之前, 客户端发送一个初始的客户端hello消息给到服务端, 之后服务端会送一个REJ消息。 REJ消息包含: 1.一个服务端的配置,这包括服务端的长期的迪菲-郝夫曼算法的公开值。 2.对服务器进行身份认证的证书链。 3. 使用证书链的叶子证书的私有key对服务端的签名。4.一个源地址token:一个认证加密的区块,其中包含了客户端的公开可见的IP地址和服务端的一个时间戳。客户端在接下来的握手中发送这个token回到服务端,验证其IP地址的所有权。 一旦客户端接收到服务端的配置, 通过验证证书链和签名来认证这个配置。 之后客户端再发送一个complete CHLO, 其中包含了客户端的暂存的迪菲-郝夫曼的公开值。

    最终握手: 一个连接的所有key都是使用Diffie-Hellman算法来建立的。 在发送一个complete CHLO之后, 客户端就拥有了连接所需要的initial keys,这是因为这些key能够通过服务端的长期Diffie-Hellman公开值以及客户端自己暂存的Diffie-Hellman私有值。 这样,客户端就能马上发送应用程序的数据到服务端了。 实际上,要实现对于数据0-RTT的延迟,那么必须在服务端发送回复之前就将用initial keys加密好的数据发送出去。

    如果握手成功了, 服务端返回一个server hello(SHLO)消息。 这个消息用initial keys进行了加密, 并且含有服务端暂存的Diffie-Hellman的公开值。


未完待续。。。

推荐阅读更多精彩内容