# 新一代低延时网络传输协议QUIC
# QUIC协议概述
Quic全称quick udp internet connection,“快速UDP互联网连接”,是一种利用udp进行多路并发传输的协议
QUIC是用来替代TCP、SSL/TLS的传输层协议,常用的HTTP、FTP、IMAP等应用层协议理论上都可以运行在QUIC上
# QUIC的核心特性
# 建立连接时延低
QUIC协议具有0RTT的特点。
从左图可以看出,传统TCP连接后HTTP发送数据需要3RTT,而使用QUIC,只需要首次连接1RTT,后续连接只需0RTT,意味着客户端发给服务端的第一个包就带有请求数据。
从右图看,QUIC的请求过程分为以下几步
首次连接时,客户端发送Inchoate Client Hello 给服务端,用于请求连接
服务端生成g、p、a,根据g、p、a计算出A,然后将g、p、A放到Server Config中再发送Rejection消息给客户端
客户端收到g、p、A,自己生成b,根据g、p、b算出B,根据A、p、b算出初始密钥K。B和K算好后,客户端用K加密HTTP数据,连同B一起发送给服务端
服务端收到B后,根据a、p、B生成与客户端相同的密钥,再用这个密钥解密收到的HTTP数据。为了进一步的安全,服务端会更新自己的随机数a和公钥,再生成新的密钥S,然后把公钥通过Server Hello发送给客户端,连同Server Hello消息,还有HTTP返回数据
客户端收到Server Hello后,生成与服务端一致的新密钥S,后面的传输都使用S加密
整个过程,QUIC从请求连接到发送HTTP数据一共花了1RTT,这1个RTT主要是获取Server Config,后面的连接如果客户端缓存了ServerConfig,那么就可以直接发送HTTP数据,实现0RTT建立连接
# 连接迁移
TCP连接基于四元组(源IP、源端口、目的IP、目的端口),切换网络一个元素发生变化,则连接发生变化。
如果连接发生变化时,还使用原来的TCP连接,则会连接失败,就必须等连接超时后重新建立连接
QUIC的连接不受四元组的影响,当四元组发生变化时,原来的连接状态仍然保持
其原理是,QUIC的连接不以四元组作为标识,而是使用一个64位的随机数,这个随机数成为你的Connection ID,即使IP或端口发生变化,只要这个Connection ID没有变化,仍然可以维持连接
# 多路复用/队头阻塞
# 队头阻塞
TCP是面向连接的协议,每次发送请求后,都需要收到ACK来保证对方接收到数据。如果每次请求都要在收到上次的ACK后再请求,那么效率会很低,之后Http1.1提出了Pipelining技术,允许一个TCP连接同时发送多个请求,提高了传输效率
但是,Pipelining会带来队头阻塞问题
在上图中,一个TCP连接中,同时传输10个请求,其中1、2、3个请求被接收,但第4个请求丢失,那么后面的请求会发生阻塞,浪费了带宽资源
# 多路复用
在Http/2中,多路复用技术解决了上述队头阻塞的问题
不像http/1.1中只有上一个请求的所有数据包被传输,Http/2中每个请求被拆分为多个Frame,通过一条TCP
连接同时被传输,这样即使一个请求阻塞,也不会影响其他请求,不同颜色代表不同请求,相同颜色的色块代表请求被切分的frame
Http/2解决了“请求”这个粒度的阻塞,但是其基础协议TCP也会存在队头阻塞的问题。HTTP/2的每个请求都会被拆分成多个frame。不同请求的frame组合成stream,stream是TCP上的逻辑传输单元,这样 HTTP/2 就达到了一条连接同时发送多条请求的目标,这就是多路复用的原理。
如上图所示,在一条TCP连接上同时发送4个Stream,其中1到达,2中的第三个frame丢失,因此会要求重发第三个frame,3、4已经到达却不能被处理,整条链路被阻塞。
而且Http/2必须使用Https,因此其中的TLS协议也会存在队头阻塞的问题。
QUIC解决队头阻塞有两点:
- QUIC的传输单元是Packet,加密单元也是Packet,整个加密、传输、解密都基于Packet,因此可以避免TLS队头阻塞的问题
- QUIC基于UDP,UDP的数据包在接收端没有处理顺序,因此中间丢失一个包,也不会阻塞整条连接,其他资源会被正常处理
# 拥塞控制
拥塞控制的目的是避免过多的数据一下子进入网络,导致网络超出负荷。QUIC的拥塞控制与TCP类似,并且在此基础上做了改进,TCP的拥塞控制由4个核心算法组成
- 慢启动:发送方向接收方发送1个单位的数据,收到对方确认后会发送2个单位的数据,然后依次是4个8个……呈指数级增长,这个过程就是在不断试探网络的拥塞程度,超出阈值则会导致网络拥塞;
- 拥塞避免:指数增长不可能是无限的,到达某个限制(慢启动阈值)之后,指数增长变为线性增长;
- 快速重传:发送方每一次发送时都会设置一个超时计时器,超时后即认为丢失,需要重发;
- 快速恢复:在上面快速重传的基础上,发送方重新发送数据时,也会启动一个超时定时器,如果收到确认消息则进入拥塞避免阶段,如果仍然超时,则回到慢启动阶段。
QUIC重新实现了TCP协议的Cubic算法,并做了改进。
# 热插拔
TCP 中如果要修改拥塞控制策略,需要在系统层面进行操作。QUIC 修改拥塞控制策略只需要在应用层操作,并且 QUIC 会根据不同的网络环境、用户来动态选择拥塞控制算法。
# 单调递增的Packet Number
TCP 为了保证可靠性,使用 Sequence Number 和 ACK 来确认消息是否有序到达,但这样的设计存在缺陷。
当产生重传后,收到ACK的确认消息,此时无法确定此ACK是第一次传输还是重传的确认信息
而QUIC的Packet Number是单调递增的,因此如果Packet N丢失了,重传时的Packet标识不会是N,而是大于N的数字,这样发送方就能清楚的知道是原始请求还是重传请求的确认信息
# ACK Delay
TCP 计算 RTT 时没有考虑接收方接收到数据到发送确认消息之间的延迟。QUIC 考虑了这段延迟,使得 RTT 的计算更加准确。
# 更多ACK块
一般来说,接收方收到发送方的消息后都应该发送一个 ACK 回复,表示收到了数据。但每收到一个数据就返回一个 ACK 回复太麻烦,所以一般不会立即回复,而是接收到多个数据后再回复,TCP SACK 最多提供 3 个 ACK block。但有些场景下,比如下载,只需要服务器返回数据就好,但按照 TCP 的设计,每收到 3 个数据包就要“礼貌性”地返回一个 ACK。而 QUIC 最多可以捎带 256 个 ACK block。在丢包率比较严重的网络下,更多的 ACK block 可以减少重传量,提升网络效率。
# 流量控制
TCP会对每个连接进行流量控制,让发送方不要发送太快,要让接收方来得及接收,不然会导致数据溢出而丢失。
TCP的流量控制主要通过滑动窗口实现。拥塞控制主要是控制发送方的发送策略,流量控制考虑了接收方的接收能力。
QUIC 只需要建立一条连接,在这条连接上同时传输多条 Stream。QUIC 的流量控制有两个级别:连接级别(Connection Level)和 Stream 级别(Stream Level),既要控制这条路的总流量,不要一下子很多车辆涌进来,货物来不及处理,也不能一个车辆一下子运送很多货物,这样货物也来不及处理。
单条 Stream 的流量控制。Stream 还没传输数据时,接收窗口(flow control receive window)就是最大接收窗口(flow control receive window),随着接收方接收到数据后,接收窗口不断缩小。在接收到的数据中,有的数据已被处理,而有的数据还没来得及被处理。
当越来越多的数据被处理后,可接收的数据又会增加,开始接收更多数据
# 总结
QUIC基于UDP,借鉴了TCP、TLS、HTTP/2的特性,但更简洁,具有零RTT建立连接、连接迁移、多路复用、拥塞控制、流量控制等优秀的特点,是一个更安全高效可靠的HTTP通信协议。