HTTP/3竟然基于UDP,QUIC渐成主流
2019-04-13 11:38:51 阿炯

听到 HTTP/3 基于 UDP 协议的消息,不少人可能都跟我一样惊呆了。

我们从开始学习网络协议就一定会接触到 HTTP,而教科书或者老师一直以来说的都是“UDP 不可靠,所以 HTTP 基于 TCP 协议”,虽然偶尔会思考“UDP 与 TCP 都是比较底层的协议,用 TCP 来定义上层的 HTTP 协议,也是需要经过一系列设计和封装的,那凭什么 UDP 就不可以试试呢?”、“是成本问题?HTTP 在 TCP 之上设计的成本也不低啊,比如三次握手、四次挥手、滑动窗口等构思精妙的算法,也都是在经过无数次设计与尝试之后确定下来的。”……但是总之 HTTP 只能基于 TCP,而不能是 UDP 这一思维还是在一道道试题和一次次编程 request-response 的过程中固定在脑海里。

所以 HTTP/3 不再基于 TCP 而是采用了 UDP,这一消息还是挺让人惊讶的。看到这里可能有人会惊讶于另一个点:什么?!HTTP 协议都发展到 v3 了?

其实目前正逐渐走向主流的 HTTP 协议是 HTTP/2,它相比于 HTTP/1,大幅度提高了性能,网站只需要升级到新版本协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级是比较棘手的问题,这也应该是国内目前还不普遍使用 HTTP/2 的重要原因。虽然 HTTP/2 带来了许多优点,但是并不代表它已经是完美的了,HTTP/3 就是为了解决 HTTP/2 所存在的一些问题而被推出来的。

本文接下来会从基础的 HTTP/1 开始讲起,从第一代协议到第三代分别针对性地介绍,试图把 HTTP 这一协议的技术发展过程以简要通俗的方式分享给读者,并让大家明白,为什么经过这么多年的发展,HTTP 协议最终竟然采用了不安全的 UDP。

一、HTTP 协议

HTTP 是 HyperText Transfer Protocol(超文本传输协议)的缩写,它是互联网上应用最为广泛的一种网络协议,所有 Web 文件都必须遵守这个标准。

伴随着计算机网络和浏览器的诞生,HTTP 1.0/1.1 也随之而来,它建立在 TCP 协议之上,处于计算机网络中的应用层,所以 HTTP 协议的瓶颈及其优化技巧都是基于 TCP 协议本身的特性,例如 TCP 建立连接的 3 次握手和断开连接的 4 次挥手,以及每次建立连接带来的 RTT 延迟时间等。

HTTP 1.0 与 1.1 的主要区别在于长连接支持、多路复用、带宽节约与数据压缩等,相对于 HTTP/2,本文将其通称为 HTTP/1。

二、HTTP/1 的缺陷

HTTP/1 在 Web 时代迅速崛起,但是随着采用日涨,其缺陷也暴露出来;不论是 1.0 还是 1.1 版本,HTTP/1.x 都主要存在以下几个方面的缺陷:

连接无法复用:连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对大量小文件请求影响较大(没有达到最大窗口请求就被终止)。
    HTTP/1.0 传输数据时,每次都需要重新建立连接,增加延迟。
    HTTP/1.1 虽然加入 keep-alive 可以复用一部分连接,但域名分片等情况下仍然需要建立多个 connection,耗费资源,给服务器带来性能压力。

Head-Of-Line Blocking(HOLB,队头阻塞):这会导致带宽无法被充分利用,以及后续健康请求被阻塞。HOLB 是指一系列包(package)因为第一个包被阻塞;当页面中需要请求很多资源的时候,HOLB 会导致在达到最大请求数量时,剩余的资源需要等待其它资源请求完成后才能发起请求。
    HTTP 1.0:下个请求必须在前一个请求返回后才能发出,request-response对按序发生。显然,如果某个请求长时间没有返回,那么接下来的请求就全部阻塞了。
    HTTP 1.1:尝试使用 pipeling 来解决,即浏览器可以一次性发出多个请求(同个域名、同一条 TCP 链接)。但 pipeling 要求返回是按序的,那么前一个请求如果很耗时(比如处理大图片),那么后面的请求即使服务器已经处理完,仍会等待前面的请求处理完才开始按序返回。所以pipeling只部分解决了 HOLB。


如上图所示,红色圈出来的请求就因域名链接数已超过限制,而被挂起等待了一段时间。

协议开销大:HTTP/1 在使用时,header 里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求 header 基本不怎么变化,尤其在移动端增加用户流量。

安全因素:HTTP/1 在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。

三、SPDY 协议

因为 HTTP/1 的问题,我们会引入雪碧图、将小图内联、使用多个域名等等的方式来提高性能。不过这些优化都绕开了协议本身,直到 2009 年,谷歌公开了自行研发的 SPDY 协议,它主要解决 HTTP/1.1 效率不高的问题。直到这时,才算是正式改造了 HTTP 协议本身。SPDY 进行延迟降低、header 压缩等改进,其实践证明了这些优化的效果,也最终带来 HTTP/2 的诞生。SPDY 协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承,下面我们就来讲讲这一部分内容。

四、HTTP/2

2015 年,继承于 SPDY 的 HTTP/2 协议发布了。HTTP/2 是 HTTP/1 的替代品,但它不是重写,协议中还保留着第一代的一些内容,比如 HTTP 方法、状态码与语义等都与 HTTP/1 一样。

HTTP/2 基于SPDY3,专注于性能,最大的一个目标是在用户和网站间只用一个连接。

HTTP/2 由两个规范组成:
Hypertext Transfer Protocol version 2 - RFC7540
HPACK - Header Compression for HTTP/2 - RFC7541

五、HTTP/2 特性

二进制传输

HTTP/2 采用二进制格式传输数据,而非 HTTP/1 的文本格式,二进制协议解析起来更高效。

HTTP/1 的请求和响应报文,都是由起始行、首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。

接下来我们介绍几个重要的概念:
流(stream):流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2…N)

消息(message):指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成

帧(frame):HTTP/2 通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流,承载着特定类型的数据,如 HTTP 首部、负荷等


HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。

多路复用

在 HTTP/2 中引入了多路复用技术。多路复用很好地解决了浏览器限制同一个域名下的请求数量的问题,同时也更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。

大家可以通过这个链接直观感受下 HTTP/2 比 HTTP/1 到底快了多少。


在 HTTP/2 中,有了二进制分帧之后,HTTP/2 不再依赖 TCP 链接去实现多流并行了,像前边提到的,在 HTTP/2 中:
同域名下所有通信都在单个连接上完成
单个连接可以承载任意数量的双向数据流
数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装

这一特性,使性能有了极大提升:
同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,消除了因多个 TCP 连接而带来的延时和内存消耗
并行交错地发送多个请求,请求之间互不影响
并行交错地发送多个响应,响应之间互不干扰
在 HTTP/2 中,每个请求都可以带一个 31 bit 的优先值,数值越大优先级越低,0 表示最高优先级。有了这个优先值,客户端和服务器就可以在处理不同流时采取不同的策略,以最优的方式发送流、消息和帧。


如上图所示,多路复用技术可以只通过一个 TCP 连接传输所有的请求数据。

Header 压缩

在 HTTP/1 中,我们使用文本的形式传输 header,在 header 携带 cookie 的情况下,可能每次都需要重复传输几百到几千字节。

为了减少这块的资源消耗并提升性能, HTTP/2 对这些首部采取了压缩策略:
HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送
首部表在 HTTP/2 的连接存续期内始终存在,由客户端和服务器共同渐进地更新
每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值

例如下图中的两个请求,请求 1 发送了所有头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销:


Server Push

Server Push 即服务端能通过 push 的方式将客户端需要的内容预先推送过去,也叫“cache push”。

可以想象以下情况:某些资源客户端是一定会请求的,这时就可以采取服务端 push 的技术,提前给客户端推送必要的资源,这样就可以相对减少一点延迟时间。当然在浏览器兼容的情况下你也可以使用 prefetch。

例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 时再发送这些请求。


服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送 RST_STREAM 帧来拒收。主动推送也遵守同源策略,换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。

六、HTTP/3

虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。

上文提到 HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1 了。

因为在出现丢包的情况下,整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。

那么可能就会有人考虑到去修改 TCP 协议,其实这已经是一件不可能完成的任务了,因为 TCP 存在的时间实在太长,已经充斥在各种设备中,并且这个协议是由操作系统实现的,更新起来不大现实。

基于这个原因,Google 就自己架起炉灶搞了一个基于 UDP 协议的 QUIC 协议,并且使用在了 HTTP/3 上,HTTP/3 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。

QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,接下来我们重点介绍几个 QUIC 功能。

0RTT

通过使用类似 TCP 快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。0RTT 建连可以说是 QUIC 相比 HTTP/2 最大的性能优势。那什么是 0RTT 建连呢?

这里面有两层含义:
1、传输层 0RTT 就能建立连接。
2、加密层 0RTT 就能建立加密连接。


因为这里考虑到安全性,我们就拿加了 TLS 的“安全的 HTTP 协议”HTTPS 来对比。上图左边是 HTTPS 的一次完全握手的建连过程,需要 3 个 RTT,就算是会话复用也需要至少 2 个 RTT。

而 QUIC 呢?由于建立在 UDP 的基础上,同时又实现了 0RTT 的安全握手,所以在大部分情况下,只需要 0 个 RTT 就能实现数据发送,在实现前向加密的基础上,并且 0RTT 的成功率相比 TLS 的会话记录单要高很多。

多路复用

QUIC 原生实现了多路复用功能,并且传输的单个数据流可以保证有序交付且不会影响其它数据流,这样的技术就解决了前边提到的 TCP 多路复用存在的问题。

同 HTTP/2 一样,同一个 QUIC 连接上可以创建多个 stream 来发送多个 HTTP 请求,但QUIC是基于 UDP 的,因为一个连接上的多个 stream 之间没有依赖,所以不存在 HTTP/2 中的问题。比如下图中 stream2 丢了一个 UDP 包,不会影响后面跟着 Stream3 和 Stream4,不存在 TCP 队头阻塞。虽然 stream2 的那个包需要重新传,但是 stream3、stream4 的包无需等待就可以发给用户。


另外 QUIC 在移动端的表现也会比 TCP 好。因为 TCP 是基于 IP 和端口去识别连接的,这种方式在多变的移动端网络环境下是很脆弱的。而 QUIC 是通过 ID 的方式去识别一个连接,不管你网络环境如何变化,只要 ID 不变,就能迅速重连上。

加密认证的报文

TCP 协议头部没有经过任何加密和认证,所以在传输过程中很容易被中间网络设备篡改、注入和窃听,比如修改序列号与滑动窗口。这些行为有可能是出于性能优化,也有可能是主动攻击。

相比之下,QUIC 的 packet 可以说是武装到了牙齿。除了个别报文比如 PUBLIC_RESET 和 CHLO,所有报文头部都是经过认证的,报文 Body 都是经过加密的。

这样只要是针对 QUIC 报文进行了任何修改,接收端都能够及时发现,有效地降低了安全风险。


如上图所示,红色部分是 Stream Frame 的报文头部,有认证;绿色部分是报文内容,全部经过加密。

前向纠错机制

QUIC 协议有一个非常独特的特性,称为前向纠错(Forward Error Correction,FEC),每个数据包除了它本身的内容之外,还包括了部分其它数据包的数据,因此少量的丢包可以通过其它包的冗余数据直接组装而无需重传。前向纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传次数。这会取得更好的效果,因为数据重传将会消耗更多的时间,包括确认数据包丢失、请求重传与等待新数据包等步骤。

假如说这次我要发送三个包,那么协议会算出这三个包的异或值并单独发出一个校验包,也就是总共发出了四个包,当出现其中的非校验包丢包的情况时,可以通过另外三个包计算出丢失的数据包的内容。当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包就不能使用纠错机制了,只能使用重传的方式了。

七、总结

HTTP/1 有连接无法复用、队头阻塞、协议开销大和安全因素等多个缺陷

HTTP/2 通过多路复用、二进制流与 Header 压缩等技术,极大地提高了性能,但是还是存在一些问题

HTTP/3 抛弃 TCP 协议,以全新的视角重新设计 HTTP。其底层支撑是 QUIC 协议,该协议基于 UDP,有 UDP 特有的优势,同时它又取了 TCP 中的精华,实现了即快又可靠的协议

从 HTTP/1 到 HTTP/3,HTTP 协议经过不断进化,性能越来越高,在这个过程中,底层协议甚至从 TCP 改为了之前被认定为不适合 UDP,这其中不断探索的设计思想值得学习。虽然本文是简单的介绍,但已经把这一演进过程简单地总结了出来,希望读者能够有所收获。

作者介绍

浪里行舟,专注于前端领域。个人公众号:前端工匠,致力于推送适合初中级工程师快速吸收的优质文章。上文链接


多说说QUIC

QUIC的发音类似于Quick,实际上也确实很快。它可以很好地解决应用在传输层和应用层面临的各种需求,包括处理更多的连接、安全性以及低延迟。目前在互联网领域,QUIC可以说刮起了新一代互联网传输协议的风。对开发者而言,了解QUIC更是有助于时延敏感性应用以及音视频、购物支付等应用场景的体验提升。

1、QUIC拥有两大优势

**0RTT,建立低延迟传输**

传统的TLS协议,需要经过两级握手实现用户数据的传输。第一级包括TCP的三次握手,至少需要一个来回;第二级是TLS协议的握手,通过ClienHello、ServerHello几次握手的数据包协商后才能开始用户数据传输。虽然TLS1.3在TLS握手阶段进行了优化,支持在首包ClientHello传输数据,但TCP的握手还是无法节省。QUIC协议则抛弃了TCP协议,改用UDP作为底层传输协议,进一步压缩了TCP三次握手所带来的时延,达到了真正的0RTT。这一优势对时延敏感型的应用很有吸引力,也给视频类应用提供了切换至QUIC协议的动力。

**加密传输**

大部分互联网公司都十分注重用户的安全隐私,始终持续推进数据的加密传输。这项工作需要两个协议支撑,分别是HTTP协议和DNS协议:
(1)HTTP协议从1.1版本升级到2.0再到3.0,本身并没有涉及加密的内容,仅在时延问题上改进。但与HTTP协议伴生的TLS协议专职进行加密,从TLS1.2升级到了TLS1.3,不仅增强了加密的强度,还将原先的明文握手部分进行了大幅加密。甚至,TLS协议计划未来将所有的握手部分均加密。
(2)DNS协议与HTTP协议也是伴生状态,但不可避免的会泄露HTTP协议中的域名信息。因此,DNS的加密一般会同时进行。

目前主流的解决方案是使用TLS进行加密,但QUIC协议拥有和TLS类似的加密能力,且性能更好。这打破了TLS协议对加密的垄断,成为其最大竞争者。

2、QUIC的使用情况

在多年前,谷歌和Meta(原Facebook)对QUIC协议分别进行了研究,甚至Facebook还实现了一个TCP版本的QUIC。后来他们在研究上分列两个阵营,一个是谷歌的gQUIC,另一个是IETF-QUIC。不过最后达成了一致,均归为IETF-QUIC阵营,也就是现今QUIC的雏形。

作为主推者,谷歌和Facebook旗下的App已大量使用QUIC进行通信。那么如今他们以及各大互联网厂商都在QUIC上有哪些进展呢?
谷歌:作为使用广泛的移动操作系统Android,其自带浏览器组件Webview均默认支持QUIC,Chrome及其衍生浏览均支持QUIC。还有一些和用户生活连接紧密的App也会尝试使用QUIC,比如Youtube、Gmail、Google map、Google Play等。这些在支持使用的场景下都会默认进行QUIC的传输。

Facebook:Facebook、Messenger、Instagram、Whatsapp等旗下较为知名的App和谷歌使用类似的QUIC策略。

Apple:苹果在QUIC上的策略没有那么激进,但已经将QUIC作为未来趋势进行准备,包括QUIC上线所配套的DoH服务器。另外,苹果已经在最新的iCloud+ Private Relay中使用了QUIC作为代理传输协议。

CloudFlare:作为一个CDN厂商,ClouFlare一直大力推动QUIC的使用,覆盖大量chrome+小网站模式下的流量,让这些流量默认使用QUIC。

Snapchat:跟随着Google的脚步,这款较为风靡的聊天软件,也大量使用了QUIC。

国内互联网厂商:快手、搜狐视频主力使用QUIC传输视频,目前是国内推进最快的。微信、淘宝、爱奇艺、抖音、百度已在部分流量或者部分时延场景下启用QUIC。使用QUIC逐渐成为国内互联网厂商的潮流。

3、QUIC协议格式

经过长时间的演变以及两个阵营的研究,QUIC协议具有很多分支和变种。这里我们省略一些前期变化的叙述,聚焦当前的情况展开。目前,QUIC协议主要有两大分支版本。

gQUIC版本,由谷歌打造并广泛使用。QUIC的载荷内容能够看到的只有ClientHello包和Rejection包,其他的数据包均是加密的,没有秘钥看不到。因此我们先介绍一下暴露在外面的内容,如下图是gQUIC的ClientHello包结构,在wireshark里面显示的是IETF QUIC。这是因为两个分支正在融合,在这部分是基本一致的,包括包头、CRYPTO和PADDING三部分。包头是一些基本信息,重要的是版本号和Connection ID。


CRYPTO包含具体的握手参数,这是与gQUIC和IETF QUIC区别最大的地方。但它们的作用类似,都是提供域名、加密参数之类的握手所需要的信息。下图则是gQUIC中的格式,是谷歌自己定义的:


在IETF QUIC里的CRYPTO装的是一个TLS的ClientHello,基本上直接复制了TLS的格式。下图是IETF QUIC的CRYPTO格式,从外部格式看这是两个QUIC分支最大的区别点:


外部能看到的格式介绍到这里,已经说明了90%,其他部分在Wireshark里面有比较明确的解释。此外,最新版的QUIC(两个分支)均使用了Encrypted ClientHello机制,前面介绍的ClientHello在流量里面是“加密”状态,看起来是一些随机的字节,只有最开始的几个字节用来区分不同的QUIC版本。但这个“加密”的秘钥就藏在ClientHello包里面,可以现场计算出真正的秘钥并解密。因此,Wireshark能够看到明文的ClientHello内容。这种“加密”类似当年的P2P协议,都是为了增加DPI设备的处理难度,最终需要拼CPU算力。如果CPU算力不够则看不到明文。

4、QUIC的交互过程

Wireshark提供了QUIC流量的解密功能,有秘钥就能看到加密前的具体内容。这样我们也就能直观的看到QUIC的交互过程。事实上,QUIC承担了TCP的功能,主要是可靠性传输的保障能力。从下图可以看出,内部会传输大量的ACK报文,用来确认数据已经收到,基于此再产生重传等拥塞控制相关的能力。


除了可靠性传输的保障能力,QUIC内部存在stream机制。每个stream都可以被认为是一个独立的流,这样QUIC本身就是一个大的加密传输隧道。QUIC内部实际传输数据的协议一般是HTTP3,这让QUIC和HTTP3产生了强绑定,很多时候大家会把这二者当成是一个东西。Wireshark目前并没有解析HTTP3,只能看到一些二进制的数据。但HTTP3继承了HTTP2,数据带有压缩,短短的几个字节可能就是一个巨大请求压缩后的结果。


综上所述,QUIC协议是一个结合多种优秀特性的互联网传输新协议,自然也成为了互联网各大厂商的新宠儿。对此,华为也推出了HMS Core网络加速套件——hQUIC Kit,帮助开发者在应用中快速支持QUIC协议,再辅以智能拥塞算法,最终为用户提供更快的连接建立速度,更强的抗丢包能力以及更高的吞吐量。hQUIC适用游戏、视频通话、在线TV/VOD、VR实时广播等应用场景,其服务优势有:
简单易用:提供简单易用的编程接口,屏蔽网络细节。
兼容性:兼容gQUIC协议,支持Cronet接口。
移动网络体验提升:提升弱网环境对用户的体验。


阿里自研标准化协议库 XQUIC 于2022年1月上旬正式开源

XQUIC 是什么

XQUIC是阿里自研的IETF QUIC标准化传输协议库。XQUIC是基于IETF QUIC协议实现的UDP传输框架,包含加密可靠传输、HTTP/3两大块主要内容,为应用提供可靠、安全、高效的数据传输功能,可以极大改善弱网和移动网络下产品的用户网络体验。这项技术研发由大淘宝平台技术团队发起和主导,当前有达摩院XG实验室、阿里云CDN等多个团队参与其中。

现今QUIC有多家开源实现,为什么选择标准协议 + 自研实现的道路?我们从14年开始关注Google在QUIC上的实践(手机淘宝在16年全面应用HTTP/2),从17年底开始跟进并尝试在电商场景落地GQUIC,在18年底在手淘图片、短视频等场景落地GQUIC并拿到了一定的网络体验收益。然而在使用开源方案的过程中,或多或少碰到了一些问题,例如包大小过大、依赖复杂等等。最终促使走上自研实现的道路。

为什么要选择IETF QUIC标准化草案的协议版本?过去也尝试过自研私有协议,在端到端都由内部控制的场景下,私有协议的确是很方便的,并且能够跟随业务场景的需求快速迭代演进;但私有协议方案很难走出去建立一个生态圈/或者与其他的应用生态圈结合(遵循相同的标准化协议实现互联互通);另一方面从云厂商的角度,私有协议也很难与外部客户打通;同时由于IETF开放讨论的工作模式,协议在安全性、扩展性上会有更全面充分的考量。因此我们选择IETF QUIC标准化草案版本来落地。截止目前,IETF工作组已经发布QUIC v1版本RFC,XQUIC已经支持该版本,并能够与其他开源实现基于QUIC v1互通。

IETF QUIC: 指IETF工作组推进的QUIC标准,包括RFC8999~9002,以及还在推进中的HTTP/3.0、QPACK等一系列内容。


XQUIC 的优势


XQUIC是一个轻量、高性能、标准化的跨平台协议库:

轻量性:
XQUIC在Android/iOS双端的编译产物均小于400KB
除TLS/1.3能力依赖SSL库之外,无其他外部依赖,可以方便地部署到移动设备和各种嵌入式设备中
适用于需要高性能但同时又对包大小敏感的移动端APP场景(为了减少新用户的安装成本,移动端APP希望能尽量减少APP包大小)

高性能传输:
XQUIC已经在手机淘宝实现核心导购、短视频链路大规模使用,并相对于内核态TCP+HTTP/2优化20%的网络请求耗时
支持0-RTT功能
支持多通道传输加速能力

标准化:
XQUIC实现了整套IETF QUIC标准协议,包含传输层、加密层、应用层协议栈
协议版本支持QUIC version 1,以及draft-29
SSL库兼容适配BoringSSL或BabaSSL(可任意选择其中之一)

易用性:
跨平台:支持Linux/Android/iOS/Mac等平台,后续也会支持Windows平台适配,客户端可以通过SDK方式很方便地接入并使用。
支持Wireshark解析、qlog事件日志标准,方便问题排查
完善的文档(中文/英文对照)、demo示例和单测

XQUIC核心介绍

模块设计

XQUIC是IETF QUIC草案版本的一个C协议库实现,端到端的整体链路架构设计如下图所示。XQUIC内部包含了QUIC-Transport(传输层)、QUIC-TLS(加密层、与TLS/1.3对接)和HTTP/3.0(应用层)的实现。除了每层的协议栈功能模块之外,在公共模块部分,XQUIC也支持了qlog[5]日志标准。


拥塞控制算法框架


拥塞控制算法模块,在传输协议栈中承担了发动机的职能。为了能够方便地实现多套拥塞控制算法、并方便针对各类典型场景进行优化,我们将拥塞控制算法流程抽象成7个回调接口,其中最核心的两个接口onAck和onLost用于让算法实现收到报文ack和检测到丢包时的处理逻辑。XQUIC内部实现了多套拥塞控制算法,包括最常见的Cubic、New Reno,以及时下比较流行的BBR v1和v2,每种算法都只需要实现这7个回调接口即可实现完整算法逻辑。

为了方便用数据驱动网络体验优化,我们将连接的丢包率、RTT、带宽等信息通过采样和分析的方式,结合每个版本的算法调整进行效果分析。同时在实验环境下模拟真实用户的网络环境分布,更好地预先评估算法调整对于网络体验的改进效果。

传输层能力和应用协议协商

XQUIC提供两套接口,分别是使用标准HTTP3的7层接口和直接使用传输层能力的4层接口,同时XQUIC支持ALPN协商机制,可以通过向ALPN接口注册新的应用层协议回调,并通过握手期间的协商实现多套应用层协议的兼容。


7层协议的扩展能力和易用性: XQUIC的接口中,将QUIC Transport事件归类为通用传输层事件和面向应用层协议的事件。连接会话、Stream事件面向Application-Layer-Protocol定义;而剩余的通用传输层事件,因为在不同的应用层协议之间,具备高度共性,可以复用。这种设计保证了在扩展多种7层协议的时候,开发者只需要关注7层协议对于连接会话、Stream数据的处理,而不需要重复对QUIC传输层通用事件进行开发。

TLS层设计


QUIC Transport层,对TLS模块有如下依赖:加密握手协商、数据加解密、密钥更新、session resumption、0-RTT、传输参数、ALPN协商。TLS层,则需要依赖底层SSL库,来支撑上述功能。因此,TLS模块存在数据的多样性,以及依赖的多样性,数据流程和代码结构会比较复杂。TLS层需要对这些数据流进行归类整理,从而来简化上下游的依赖关系,降低代码的复杂度。

XQUIC适配了babassl、boringssl两种底层的ssl库,向上提供统一的接口,从而消除了它们之间接口、流程的差异,并抽象为统一的内部数据流程,仅针对不同ssl库提供轻薄的适配层,减少重复适配的代码逻辑,达到降低代码复杂度、提升可维护性的效果。同时XQUIC也提供了编译选项,方便开发者根据自身应用的情况,选择适合自己的依赖库。


XQUIC开源历史

为什么要做XQUIC


从2018年左右,开始探索从TCP转向UDP方向,最早是基于GQUIC,主要应用在手淘的图片和短视频等内容分发的场景。在18年底19年初,当时大家有一个共同的判断是要走标准化道路,一方面整个标准化协议的设计和安全性都有更完备的考量,另一方面是因为从网络加速产品角度,私有协议解决方案更难被用户认可。在决定选择标准化道路之后,当时市面上也没有特别成熟并适用于移动端的IETF QUIC协议栈实现,所以手淘就启动了自研XQUIC项目。

经过1年半的研发和打磨,于20年的6月份开始全面上线,并在20年8月在手淘核心导购RPC请求场景进行规模化验证。在21年初与CDN IETF QUIC产品实现对接,并在短视频场景上开始逐步应用IETF QUIC技术。在去年的9月份我们实现了IETF QUIC整套协议栈在短视频场景下的规模化应用。之后,我们经历了2021年双十一的考验,XQUIC的性能和稳定性都有了很好的验证,因此在今年的1月7号,我们完成XQUIC的对外开源,后续也将持续更新迭代开源版本。

为什么要开源XQUIC

通过开源可以帮助整个社区更好地了解这项技术,可以帮助我们改进,同时可以通过社区的影响力对这项技术加以推广。社区的反馈也能够帮助我们吸收更多的需求场景输入,帮助我们更好地迭代这项技术。我们期望XQUIC在服务于淘宝技术的同时积极回馈社会,也欢迎网络技术研发的爱好者加入开源社区与我们交流。

应用场景和效果


目前,XQUIC已经在手淘Android/iOS双端正式版本、以及集团统一接入网关大规模应用,比如打开手机淘宝的首页,或是搜索我们感兴趣的商品,或是打开逛逛浏览达人的视频,XQUIC都为这些场景提供更快的网络数据传输,每天稳定为超过百亿量级的网络请求提供端到端加速能力。在2021年的双十一购物节中,XQUIC在核心导购链路、短视频场景下也经过了大规模验证。

后续Roadmap

计划每1~2个月发布一个稳定版本,当前计划如下:

新功能特性方面:
互通性功能补充,包括Key update、Retry以及ECN
WG draft版本的多路径功能支持
适配开源Tengine的module支持
非可靠传输datagram支持
Masque特性支持

由于当前Multi-path QUIC草案正处于即将被IETF QUIC Working Group接收的流程中,并且WG draft版本与XQUIC前期支持的多路径版本有部分差异,因此暂时在开源版本去掉了这部分功能。后续将在2月份基于Working Group草案版本更新多路径功能。

性能优化方面:
UDP性能优化feature
XUDP适配支持

跨平台支撑方面:
windows平台支持

配套工具方面:
网络性能测量工具

文档及中文资料:
开源仓库已经提供了基于draft-34校订的草案中文翻译,后续会陆续更新RFC8999-9002的中文译版

团队介绍:
XQUIC团队隶属于大淘宝平台技术-移动技术中台团队,希望能通过网络技术演进给用户带来更好的体验。如果对XQUIC、网络技术、高性能网络传输等领域比较感兴趣,欢迎关注GitHub仓库