QUIC协议原理分析(转)

以前深刻了解了一下HTTP1.一、2.0、SPDY等协议,发现HTTP层怎么优化,始终要面对TCP自己的问题。因而了解到了QUIC,这里分享一篇以前找到的有意义的文章。算法

原创地址:https://mp.weixin.qq.com/s/vpz6bp3PT1IDzZervyOfqw
windows

做者介绍:lancelot,腾讯资深研发工程师。目前主要负责腾讯 stgw(腾讯安全云网关)的相关工做,总体推动腾讯内部及腾讯公有云,混合云的七层负载均衡及全站 HTTPS 接入。对 HTTPS,SPDY,HTTP2,QUIC 等应用层协议、高性能服务器技术、云网络技术、用户访问速度、分布式文件传输等有较深的理解。安全

 

本文将主要介绍 QUIC 协议产生的背景和核心特性。性能优化

写在前面服务器

 

若是你的 App,在不须要任何修改的状况下就能提高 15% 以上的访问速度。特别是弱网络的时候可以提高 20% 以上的访问速度。网络

若是你的 App,在频繁切换 4G 和 WIFI 网络的状况下,不会断线,不须要重连,用户无任何感知。若是你的 App,既须要 TLS 的安全,也想实现 HTTP2 多路复用的强大。并发

若是你刚刚才据说 HTTP2 是下一代互联网协议,若是你刚刚才关注到 TLS1.3 是一个革命性具备里程碑意义的协议,可是这两个协议却一直在被另外一个更新兴的协议所影响和挑战。负载均衡

若是这个新兴的协议,它的名字就叫作“快”,而且正在标准化为新一代的互联网传输协议。ssh

你愿意花一点点时间了解这个协议吗?你愿意投入精力去研究这个协议吗?你愿意全力推进业务来使用这个协议吗?tcp

QUIC 概述

 

Quic 全称 quick udp internet connection [1],“快速 UDP 互联网链接”,(和英文 quick 谐音,简称“快”)是由 google 提出的使用 udp 进行多路并发传输的协议。

Quic 相比如今普遍应用的 http2+tcp+tls 协议有以下优点 [2]:

减小了 TCP 三次握手及 TLS 握手时间。

改进的拥塞控制。

避免队头阻塞的多路复用。

链接迁移。

前向冗余纠错。

 

为何须要 QUIC

 

从上个世纪 90 年代互联网开始兴起一直到如今,大部分的互联网流量传输只使用了几个网络协议。使用 IPv4 进行路由,使用 TCP 进行链接层面的流量控制,使用 SSL/TLS 协议实现传输安全,使用 DNS 进行域名解析,使用 HTTP 进行应用数据的传输。

并且近三十年来,这几个协议的发展都很是缓慢。TCP 主要是拥塞控制算法的改进,SSL/TLS 基本上停留在原地,几个小版本的改动主要是密码套件的升级,TLS1.3[3] 是一个飞跃式的变化,但截止到今天,尚未正式发布。IPv4 虽然有一个大的进步,实现了 IPv6,DNS 也增长了一个安全的 DNSSEC,但和 IPv6 同样,部署进度较慢。

随着移动互联网快速发展以及物联网的逐步兴起,网络交互的场景愈来愈丰富,网络传输的内容也愈来愈庞大,用户对网络传输效率和 WEB 响应速度的要求也愈来愈高。

一方面是历史悠久使用普遍的古老协议,另一方面用户的使用场景对传输性能的要求又愈来愈高。以下几个由来已久的问题和矛盾就变得愈来愈突出。

协议历史悠久致使中间设备僵化。

依赖于操做系统的实现致使协议自己僵化。

创建链接的握手延迟大。

队头阻塞。

这里分小节简单说明一下:

 

中间设备的僵化

多是 TCP 协议使用得过久,也很是可靠。因此咱们不少中间设备,包括防火墙、NAT 网关,整流器等出现了一些约定俗成的动做。

好比有些防火墙只容许经过 80 和 443,不放通其余端口。NAT 网关在转换网络地址时重写传输层的头部,有可能致使双方没法使用新的传输格式。整流器和中间代理有时候出于安全的须要,会删除一些它们不认识的选项字段。

TCP 协议原本是支持端口、选项及特性的增长和修改。可是因为 TCP 协议和知名端口及选项使用的历史太悠久,中间设备已经依赖于这些潜规则,因此对这些内容的修改很容易遭到中间环节的干扰而失败。

而这些干扰,也致使不少在 TCP 协议上的优化变得当心谨慎,寸步难行。

 

依赖于操做系统的实现致使协议僵化

TCP 是由操做系统在内核西方栈层面实现的,应用程序只能使用,不能直接修改。虽然应用程序的更新迭代很是快速和简单。可是 TCP 的迭代却很是缓慢,缘由就是操做系统升级很麻烦。

如今移动终端更加流行,可是移动端部分用户的操做系统升级依然可能滞后数年时间。PC 端的系统升级滞后得更加严重,windows xp 如今还有大量用户在使用,尽管它已经存在快 20 年。

服务端系统不依赖用户升级,可是因为操做系统升级涉及到底层软件和运行库的更新,因此也比较保守和缓慢。

这也就意味着即便 TCP 有比较好的特性更新,也很难快速推广。好比 TCP Fast Open。它虽然 2013 年就被提出了,可是 Windows 不少系统版本依然不支持它。

 

创建链接的握手延迟大

无论是 HTTP1.0/1.1 仍是 HTTPS,HTTP2,都使用了 TCP 进行传输。HTTPS 和 HTTP2 还须要使用 TLS 协议来进行安全传输。这就出现了两个握手延迟:

1.TCP 三次握手致使的 TCP 链接创建的延迟。

2.TLS 彻底握手须要至少 2 个 RTT 才能创建,简化握手须要 1 个 RTT 的握手延迟。

对于不少短链接场景,这样的握手延迟影响很大,且没法消除。

 

队头阻塞

队头阻塞主要是 TCP 协议的可靠性机制引入的。TCP 使用序列号来标识数据的顺序,数据必须按照顺序处理,若是前面的数据丢失,后面的数据就算到达了也不会通知应用层来处理。

另外 TLS 协议层面也有一个队头阻塞,由于 TLS 协议都是按照 record 来处理数据的,若是一个 record 中丢失了数据,也会致使整个 record 没法正确处理。

归纳来说,TCP 和 TLS1.2 以前的协议存在着结构性的问题,若是继续在现有的 TCP、TLS 协议之上实现一个全新的应用层协议,依赖于操做系统、中间设备还有用户的支持。部署成本很是高,阻力很是大。

因此 QUIC 协议选择了 UDP,由于 UDP 自己没有链接的概念,不须要三次握手,优化了链接创建的握手延迟,同时在应用程序层面实现了 TCP 的可靠性,TLS 的安全性和 HTTP2 的并发性,只须要用户端和服务端的应用程序支持 QUIC 协议,彻底避开了操做系统和中间设备的限制。

 

QUIC 核心特性

 

 

链接创建延时低

0RTT 建连能够说是 QUIC 相比 HTTP2 最大的性能优点。那什么是 0RTT 建连呢?这里面有两层含义。

传输层 0RTT 就能创建链接。

加密层 0RTT 就能创建加密链接。

 

图 1 HTTPS 及 QUIC 建连过程

 

好比上图左边是 HTTPS 的一次彻底握手的建连过程,须要 3 个 RTT。就算是 Session Resumption[14],也须要至少 2 个 RTT。

而 QUIC 呢?因为创建在 UDP 的基础上,同时又实现了 0RTT 的安全握手,因此在大部分状况下,只须要 0 个 RTT 就能实现数据发送,在实现前向加密 [15] 的基础上,而且 0RTT 的成功率相比 TLS 的 Sesison Ticket[13] 要高不少。

 

改进的拥塞控制

TCP 的拥塞控制实际上包含了四个算法:慢启动,拥塞避免,快速重传,快速恢复 [22]。

QUIC 协议当前默认使用了 TCP 协议的 Cubic 拥塞控制算法 [6],同时也支持 CubicBytes, Reno, RenoBytes, BBR, PCC 等拥塞控制算法。

从拥塞算法自己来看,QUIC 只是按照 TCP 协议从新实现了一遍,那么 QUIC 协议到底改进在哪些方面呢?主要有以下几点:

 

可插拔

什么叫可插拔呢?就是可以很是灵活地生效,变动和中止。体如今以下方面:

应用程序层面就能实现不一样的拥塞控制算法,不须要操做系统,不须要内核支持。这是一个飞跃,由于传统的 TCP 拥塞控制,必需要端到端的网络协议栈支持,才能实现控制效果。而内核和操做系统的部署成本很是高,升级周期很长,这在产品快速迭代,网络爆炸式增加的今天,显然有点知足不了需求。

即便是单个应用程序的不一样链接也能支持配置不一样的拥塞控制。就算是一台服务器,接入的用户网络环境也千差万别,结合大数据及人工智能处理,咱们能为各个用户提供不一样的但又更加精准更加有效的拥塞控制。好比 BBR 适合,Cubic 适合。

应用程序不须要停机和升级就能实现拥塞控制的变动,咱们在服务端只须要修改一下配置,reload 一下,彻底不须要中止服务就能实现拥塞控制的切换。

STGW 在配置层面进行了优化,咱们能够针对不一样业务,不一样网络制式,甚至不一样的 RTT,使用不一样的拥塞控制算法。

 

单调递增的 Packet Number

TCP 为了保证可靠性,使用了基于字节序号的 Sequence Number 及 Ack 来确认消息的有序到达。

QUIC 一样是一个可靠的协议,它使用 Packet Number 代替了 TCP 的 sequence number,而且每一个 Packet Number 都严格递增,也就是说就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值。而 TCP 呢,重传 segment 的 sequence number 和原始的 segment 的 Sequence Number 保持不变,也正是因为这个特性,引入了 Tcp 重传的歧义问题。

 

 

图 2 Tcp 重传歧义性

 

如上图所示,超时事件 RTO 发生后,客户端发起重传,而后接收到了 Ack 数据。因为序列号同样,这个 Ack 数据究竟是原始请求的响应仍是重传请求的响应呢?很差判断。

 

若是算成原始请求的响应,但其实是重传请求的响应(上图左),会致使采样 RTT 变大。若是算成重传请求的响应,但其实是原始请求的响应,又很容易致使采样 RTT 太小。

 

因为 Quic 重传的 Packet 和原始 Packet 的 Pakcet Number 是严格递增的,因此很容易就解决了这个问题。

 

图 3 Quic 重传没有歧义性

 

如上图所示,RTO 发生后,根据重传的 Packet Number 就能肯定精确的 RTT 计算。若是 Ack 的 Packet Number 是 N+M,就根据重传请求计算采样 RTT。若是 Ack 的 Pakcet Number 是 N,就根据原始请求的时间计算采样 RTT,没有歧义性。

 

可是单纯依靠严格递增的 Packet Number 确定是没法保证数据的顺序性和可靠性。QUIC 又引入了一个 Stream Offset 的概念。

 

即一个 Stream 能够通过多个 Packet 传输,Packet Number 严格递增,没有依赖。可是 Packet 里的 Payload 若是是 Stream 的话,就须要依靠 Stream 的 Offset 来保证应用数据的顺序。如错误! 未找到引用源。所示,发送端前后发送了 Pakcet N 和 Pakcet N+1,Stream 的 Offset 分别是 x 和 x+y。

 

假设 Packet N 丢失了,发起重传,重传的 Packet Number 是 N+2,可是它的 Stream 的 Offset 依然是 x,这样就算 Packet N + 2 是后到的,依然能够将 Stream x 和 Stream x+y 按照顺序组织起来,交给应用程序处理。

 

图 4 Stream Offset 保证有序性

 

不容许 Reneging

什么叫 Reneging 呢?就是接收方丢弃已经接收而且上报给 SACK 选项的内容 [8]。TCP 协议不鼓励这种行为,可是协议层面容许这样的行为。主要是考虑到服务器资源有限,好比 Buffer 溢出,内存不够等状况。

 

Reneging 对数据重传会产生很大的干扰。由于 Sack 都已经代表接收到了,可是接收端事实上丢弃了该数据。

 

QUIC 在协议层面禁止 Reneging,一个 Packet 只要被 Ack,就认为它必定被正确接收,减小了这种干扰。

 

更多的 Ack 块

TCP 的 Sack 选项可以告诉发送方已经接收到的连续 Segment 的范围,方便发送方进行选择性重传。

因为 TCP 头部最大只有 60 个字节,标准头部占用了 20 字节,因此 Tcp Option 最大长度只有 40 字节,再加上 Tcp Timestamp option 占用了 10 个字节 [25],因此留给 Sack 选项的只有 30 个字节。

每个 Sack Block 的长度是 8 个,加上 Sack Option 头部 2 个字节,也就意味着 Tcp Sack Option 最大只能提供 3 个 Block。

可是 Quic Ack Frame 能够同时提供 256 个 Ack Block,在丢包率比较高的网络下,更多的 Sack Block 能够提高网络的恢复速度,减小重传量。

 

Ack Delay 时间

Tcp 的 Timestamp 选项存在一个问题 [25],它只是回显了发送方的时间戳,可是没有计算接收端接收到 segment 到发送 Ack 该 segment 的时间。这个时间能够简称为 Ack Delay。

这样就会致使 RTT 计算偏差。以下图:

 

能够认为 TCP 的 RTT 计算:

 

而 Quic 计算以下:

 

固然 RTT 的具体计算没有这么简单,须要采样,参考历史数值进行平滑计算,参考以下公式 [9]。

 

 

基于 stream 和 connecton 级别的流量控制

 

QUIC 的流量控制 [22] 相似 HTTP2,即在 Connection 和 Stream 级别提供了两种流量控制。为何须要两类流量控制呢?主要是由于 QUIC 支持多路复用。

Stream 能够认为就是一条 HTTP 请求。

Connection 能够类比一条 TCP 链接。多路复用意味着在一条 Connetion 上会同时存在多条 Stream。既须要对单个 Stream 进行控制,又须要针对全部 Stream 进行整体控制。

QUIC 实现流量控制的原理比较简单:

经过 window_update 帧告诉对端本身能够接收的字节数,这样发送方就不会发送超过这个数量的数据。

经过 BlockFrame 告诉对端因为流量控制被阻塞了,没法发送数据。

QUIC 的流量控制和 TCP 有点区别,TCP 为了保证可靠性,窗口左边沿向右滑动时的长度取决于已经确认的字节数。若是中间出现丢包,就算接收到了更大序号的 Segment,窗口也没法超过这个序列号。

但 QUIC 不一样,就算此前有些 packet 没有接收到,它的滑动只取决于接收到的最大偏移字节数。

 

图 5 Quic Flow Control

 

针对 Stream:

 

针对 Connection:

 

一样地,STGW 也在链接和 Stream 级别设置了不一样的窗口数。

最重要的是,咱们能够在内存不足或者上游处理性能出现问题时,经过流量控制来限制传输速率,保障服务可用性。

没有队头阻塞的多路复用

 

QUIC 的多路复用和 HTTP2 相似。在一条 QUIC 链接上能够并发发送多个 HTTP 请求 (stream)。可是 QUIC 的多路复用相比 HTTP2 有一个很大的优点。

QUIC 一个链接上的多个 stream 之间没有依赖。这样假如 stream2 丢了一个 udp packet,也只会影响 stream2 的处理。不会影响 stream2 以前及以后的 stream 的处理。

这也就在很大程度上缓解甚至消除了队头阻塞的影响。

多路复用是 HTTP2 最强大的特性 [7],可以将多条请求在一条 TCP 链接上同时发出去。但也恶化了 TCP 的一个问题,队头阻塞 [11],以下图示:

 

图 6 HTTP2 队头阻塞

 

HTTP2 在一个 TCP 链接上同时发送 4 个 Stream。其中 Stream1 已经正确到达,并被应用层读取。可是 Stream2 的第三个 tcp segment 丢失了,TCP 为了保证数据的可靠性,须要发送端重传第 3 个 segment 才能通知应用层读取接下去的数据,虽然这个时候 Stream3 和 Stream4 的所有数据已经到达了接收端,但都被阻塞住了。

不只如此,因为 HTTP2 强制使用 TLS,还存在一个 TLS 协议层面的队头阻塞 [12]。

图 7 TLS 队头阻塞

 

Record 是 TLS 协议处理的最小单位,最大不能超过 16K,一些服务器好比 Nginx 默认的大小就是 16K。因为一个 record 必须通过数据一致性校验才能进行加解密,因此一个 16K 的 record,就算丢了一个字节,也会致使已经接收到的 15.99K 数据没法处理,由于它不完整。

那 QUIC 多路复用为何能避免上述问题呢?

QUIC 最基本的传输单元是 Packet,不会超过 MTU 的大小,整个加密和认证过程都是基于 Packet 的,不会跨越多个 Packet。这样就能避免 TLS 协议存在的队头阻塞。

Stream 之间相互独立,好比 Stream2 丢了一个 Pakcet,不会影响 Stream3 和 Stream4。不存在 TCP 队头阻塞。

 

图 8 QUIC 多路复用时没有队头阻塞的问题

 

固然,并非全部的 QUIC 数据都不会受到队头阻塞的影响,好比 QUIC 当前也是使用 Hpack 压缩算法 [10],因为算法的限制,丢失一个头部数据时,可能遇到队头阻塞。

整体来讲,QUIC 在传输大量数据时,好比视频,受到队头阻塞的影响很小。

加密认证的报文

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

可是 QUIC 的 packet 能够说是武装到了牙齿。除了个别报文好比 PUBLIC_RESET 和 CHLO,全部报文头部都是通过认证的,报文 Body 都是通过加密的。

这样只要对 QUIC 报文任何修改,接收端都可以及时发现,有效地下降了安全风险。

以下图所示,红色部分是 Stream Frame 的报文头部,有认证。绿色部分是报文内容,所有通过加密。

 

链接迁移

 

一条 TCP 链接 [17] 是由四元组标识的(源 IP,源端口,目的 IP,目的端口)。什么叫链接迁移呢?就是当其中任何一个元素发生变化时,这条链接依然维持着,可以保持业务逻辑不中断。固然这里面主要关注的是客户端的变化,由于客户端不可控而且网络环境常常发生变化,而服务端的 IP 和端口通常都是固定的。

好比你们使用手机在 WIFI 和 4G 移动网络切换时,客户端的 IP 确定会发生变化,须要从新创建和服务端的 TCP 链接。

又好比你们使用公共 NAT 出口时,有些链接竞争时须要从新绑定端口,致使客户端的端口发生变化,一样须要从新创建 TCP 链接。

针对 TCP 的链接变化,MPTCP[5] 其实已经有了解决方案,可是因为 MPTCP 须要操做系统及网络协议栈支持,部署阻力很是大,目前并不适用。

因此从 TCP 链接的角度来说,这个问题是无解的。

那 QUIC 是如何作到链接迁移呢?很简单,任何一条 QUIC 链接再也不以 IP 及端口四元组标识,而是以一个 64 位的随机数做为 ID 来标识,这样就算 IP 或者端口发生变化时,只要 ID 不变,这条链接依然维持着,上层业务逻辑感知不到变化,不会中断,也就不须要重连。

因为这个 ID 是客户端随机产生的,而且长度有 64 位,因此冲突几率很是低。

其余亮点

 

此外,QUIC 还能实现前向冗余纠错,在重要的包好比握手消息发生丢失时,可以根据冗余信息还原出握手消息。

QUIC 还能实现证书压缩,减小证书传输量,针对包头进行验证等。

限于篇幅,本文再也不详细介绍,有兴趣的能够参考文档 [23] 和文档 [4] 和文档 [26]。

 

参考线索

[1]. https:///quic

[2].

[3]. E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.3”, draft-ietf-tls-tls13-21, July 03, 2017

[4]. Adam Langley,Wan-Teh Chang, “QUIC Crypto”, 20161206

[5]. https:///

[6]. Ha, S., Rhee, I., and L. Xu, "CUBIC: A New TCP-Friendly High-Speed TCP Variant", ACM SIGOPS Operating System Review , 2008.

[7]. M. Belshe,BitGo, R. Peon, “Hypertext Transfer Protocol Version 2 (HTTP/2)”, RFC 7540, May 2015

[8]. M. Mathis,J. Mahdavi,S. Floyd,A. Romanow,“TCP Selective Acknowledgment Options”, rfc2018, October 1996

[9]. V. Paxson,M. Allman,J. Chu,M. Sargent,“Computing TCP's Retransmission Timer”, rfc6298, June 2011

[10]. R. Peon,H. Ruellan,“HPACK: Header Compression for HTTP/2”,RFC7541,May 2015

[11]. M. Scharf, Alcatel-Lucent Bell Labs, S. Kiesel, “Quantifying Head-of-Line Blocking in TCP and SCTP”, July 15, 2013

[12]. Ilya Grigorik,“Optimizing TLS Record Size & Buffering Latency”, https:///2013/10/24/optimizing-tls-record-size-and-buffering-latency/, October 24, 2013

[13]. J. Salowey,H. Zhou,P. Eronen,H. Tschofenig, “Transport Layer Security (TLS) Session Resumption without Server-Side State”, RFC5077, January 2008

[14]. Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.2", RFC 5246, DOI 10.17487/RFC5246, August 2008, <http:///info/rfc5246>.

[15]. Shirey, R., "Internet Security Glossary, Version 2", FYI , RFC 4949, August 2007

[16]. 罗成,“HTTPS性能优化”, http:///cn/presentations/performance-optimization-of-https,February.2017

[17]. Postel, J., "Transmission Control Protocol", STD 7, RFC793, September 1981.

[18]. J. Postel,“User Datagram Protocol”, RFC768,August 1980

[19]. Q. Dang, S. Santesson,K. Moriarty,D. Brown.T. Polk, “Internet X.509 Public Key Infrastructure: Additional Algorithms and Identifiers for DSA and ECDSA”,RFC5758, January 2010

[20]. Bassham, L., Polk, W., and R. Housley, "Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 3279, April 2002

[21]. D.Cooper,S.Santesson, S.Farrell,S. Boeyen,R. Housley,W.Polk, “Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile”, RFC5280, May 2008

[22]. M. Allman,V. Paxson,E. Blanton, "TCP Congestion Control”,RFC5681, September 2009

[23]. Robbie Shade, “Flow control in QUIC”, #, May, 2016,

[24]. ianswett , “QUIC fec v1”, #heading=h.xgjl2srtytjt, 2016-02-19

[25]. D.Borman,B.Braden,V.Jacobson,R.Scheffenegger, Ed. “TCP Extensions for High Performance”,rfc7323, 2014

[26]. 罗成,“WEB加速,协议先行”, ,july, 2017

相关文章
相关标签/搜索