quic
QUIC概述
感谢罗成罗成。
Quic 全称 quick udp internet connection,即:快速UDP互联网链接。是由Google提出的基于UDP协议的多路并发传输协议。
QUIC诞生背景
从上世纪90年代互联网开始兴起,大部分互联网流量传输都使用经典的一些协议:使用IPV4进行路由、使用TCP进行链接层的流量控制、使用SSL/TLS保证传输安全、使用DNS进行域名解析、使用HTTP进行数据传输。然而,这么多年来,这些经典协议发展十分缓慢。
经典协议的发展
SSL/TLS协议的发展主要是对密码体系套件的升级。
IPv4协议的发展主要是扩展到IPv6,但是IPv6的普及速度十分缓慢。
DNS协议主要在安全方面实现了DNSSEC,同样普及十分缓慢。
http协议从1.0到1.1到2.0,也是不断在功能上的优化。
但是!!!随着互联网的蓬勃发展以及物联网的迅速崛起,互联网交互场景越来越丰富,网络传输的数据量越来越大,用户对网络传输的效率和WEB服务的响应速度要求也越来越高。日益增长的需求和古老的难以快速发展的协议之间的矛盾日益尖锐。主要矛盾有:
-
协议历史悠久导致中间设备的僵化
-
依赖于操作系统的实现导致协议本身僵化
-
建立链接的握手延迟太大
-
对头阻塞
中间设备的僵化
由于TCP协议的全双工通信机制,决定了TCP的安全可靠性,长久发展以来,如防火墙、NAT网关、整流器等设备已经潜移默化的形成了一套约定俗成的动作。如:有些防火墙只放行80和443端口、NAT网关在转换网络地址时重写传输层的头部,可能会导致通信双方无法使用新的传输格式、整流器和中间代理为了安全可能会删除一些不认识的字段。
所有的这些预定俗称的东西都使得TCP协议的优化变得步履维艰。
依赖操作系统
TCP是在操作系统的内核层实现的,应用程序可以使用,但是不可以修改。操作系统的升级周期特别长,这就导致了TCP协议的迭代非常缓慢。
诸如,至今(2018-09)市场上仍会看到大量的windowsXP操作系统;同时,操作系统的升级涉及到底层软件和运行时库的更新,这使得在服务器端的更新也会比较慢。
HTTP链接延迟
使用TCP链接,就必须经过“三次握手”阶段,tcp三次握手本身就会导致链接建立的延迟。如果使用HTTPS协议,还涉及到SSL会话链接的建立,这同样增加了延迟。
队头阻塞
TCP报文传输过程中,使用序列号来进行消息顺序的识别,即数据被接收后,会在TCP协议栈上进行序列号顺序检测,如果是一个失序消息,即前面的数据丢失,那么这个消息也不会被传送给应用层。
QUIC诞生
QUIC协议放弃了经典的TCP链接,而是使用udp协议,原因:快!快!快!。UDP协议本身不需要三次握手的确认,这就优化了链接建立的延迟,而不同于UDP协议,它在应用层实现了TCP的可靠性、TLS的安全性以及HTTP2.0的多路服用特性。
支持QUIC协议的客户端和服务器通信过程中,可以完全避开中间设备和操作系统的限制。
QUIC核心特性
链接建立低延迟
传统的HTTPS链接完全建立需要三个往返时间(RTT),而基于UDP协议的QUIC不需要往返时间就可以进行数据传输。
改进的拥塞控制
TCP传统的拥塞控制实际包含了:慢启动、拥塞避免、快重传和快恢复。QUIC协议目下默认使用TCP的Cubic拥塞控制算法,同时还支持CubicBytes, Reno, RenoBytes, BBR, PCC 等拥塞控制算法。
除了基于TCP一些拥塞算法之外,QUIC协议还在其他方面进行了优化,如:
-
可插拔
即非常灵活的生效、更新和停止。具体:
-
应用程序层面能实现不同的拥塞控制算法,不依赖于操作系统、内核等。而传统的TCP拥塞控制,必须要端到端的网络协议栈支持,这就又回到了操作系统、内核更新迭代速度慢、部署成本高的问题上
-
单个程序的不同链接支持不同的拥塞控制
-
应用程序不需要停止就可以实现拥塞控制的变更,即在修改完配置,进行reload之后就可以生效
-
-
单调递增的Packet Number
TCP为了保证可靠传输,引入了基于字节的序列号sequence Number和Ack,以此保证了消息的有序性和完整性。
QUIC也是一个可靠数据传输的协议,它使用Packet Number代替TCP的Seq,并且每一个PN都严格递增,即如果一个PN=N的报文丢失,重传时的报文也不会在是N了,而是比N更大的值。这样做的好处:
在TCP的重传机制下,如果Seq=N的原始请求因超时发起了重传,同样Seq=N,服务器响应后的Ack到底是针对原始请求还是重传请求?不好判断。即有两种可能:算成原始请求响应、算成重传请求响应:
如果认为是原始请求响应,但实际是重传请求响应的话(左图):会导致RTT采样太大。
如果认为是重传请求响应,但实际是原始请求响应的话(右图):会导致RTT采样太小。
在QUIC重传机制下,重传的Packet和原始的Packet值Packet Number严格递增,这个问题就很容易解决。
根据Packet Number值就可以计算出是重传响应还是原始响应,这样就可以精确计算出RTT采样值。
当然,单纯依靠Packet Number无法完全保证数据的顺序和可靠性,即怎么直到Packet Number=(N+M)是重传请求呢?QUIC又引入了Stream offset概念,一个Stream可以经过多个Packet传输,Packet Number严格递增,但是Packet里面的Payload是Stream的话,就需要依靠Stream的Offset来保证确认数据的顺序。具体例子:
如果发送端发送了Packet Number=N和N+1,其中Stream的Offset分别为x和x+y,对Packet N包进行了重传,发送的Packet Number=N+2,但是其中的Stream的Offset依然是x,这样服务器就可以根据Offset将报文的顺序重新确定,再交给应用程序处理,具体如图:
-
不允许Reneging
所谓Reneging就是就收方丢弃已经接收并且上报给 SACK选项的内容。Reneging 对数据重传会产生很大的干扰。因为 Sack 都已经表明接收到了,但是接收端事实上丢弃了该数据。QUIC 在协议层面禁止 Reneging,一个 Packet 只要被 Ack,就认为它一定被正确接收,减少了这种干扰。
-
更多的ACK块
TCP 的 Sack 选项能够告诉发送方已经接收到的连续 segment 的范围,方便发送方进行选择性重传。由于 TCP 头部最大只有 60 个字节,标准头部占用了 20 字节,所以 Tcp Option 最大长度只有 40 字节,再加上 Tcp Timestamp option 占用了 10 个字节,所以留给 Sack 选项的只有 30 个字节。
每一个 Sack Block 的长度是 8 个,加上 Sack Option 头部 2 个字节,也就意味着 Tcp Sack Option 最大只能提供 3 个 Block。
但是 Quic Ack Frame 可以同时提供 256 个 Ack Block,在丢包率比较高的网络下,更多的 Sack Block 可以提升网络的恢复速度,减少重传量。
-
Ack Delay时间
cp 的 Timestamp 选项存在一个问题,它只是回显了发送方的时间戳,但是没有计算接收端接收到 segment 到发送 Ack 该 segment 的时间。这个时间可以简称为 Ack Delay。
这就会导致RTT计算误差,如图:
此时,TCP的RTT计算:
RTT=timestamp2-timestamp1
而QUIC的计算:
RTT=simestamp2-timestamp1-AckDelay
基于stream和connection级别的流量控制
由于QUIC支持多路服用,所以QUIC完全兼容HTTP2.0的流量控制机制:
-
Stream 可以认为就是一条 HTTP 请求。
-
Connection 可以类比一条 TCP 连接。多路复用意味着在一条 Connetion 上会同时存在多条 Stream。既需要对单个 Stream 进行控制,又需要针对所有 Stream 进行总体控制。
具体的QUIC实现流量控制机制:
通过 window_update 帧告诉对端自己可以接收的字节数,这样发送方就不会发送超过这个数量的数据。
通过 BlockFrame 告诉对端由于流量控制被阻塞了,无法发送数据。
QUIC 的流量控制和 TCP 有点区别,TCP 为了保证可靠性,窗口左边沿向右滑动时的长度取决于已经确认的字节数。如果中间出现丢包,就算接收到了更大序号的 Segment,窗口也无法超过这个序列号。但是QUIC 不同,就算此前有些 packet 没有接收到,它的滑动只取决于接收到的最大偏移字节数。
针对Stream:
可用窗口=最大窗口数-接收到的最大偏移数
针对Connection:
可用窗口=stream1可用窗口+stream2可用窗口+streamN可用窗口
最重要的是,我们可以在内存不足或者上游处理性能出现问题时,通过流量控制来限制传输速率,保障服务可用性。
没有队头阻塞的多路复用
QUIC 的多路复用和 HTTP2 类似。在一条 QUIC 连接上可以并发发送多个 HTTP 请求 。但是 QUIC 的多路复用相比 HTTP2 有一个很大的优势。QUIC 一个连接上的多个 stream 之间没有依赖。这样假如 stream2 丢了一个 udp packet,也只会影响 stream2 的处理。不会影响 stream2 之前及之后的 stream 的处理。
这也就在很大程度上缓解甚至消除了队头阻塞的影响。
多路复用是 HTTP2 最强大的特性,能够将多条请求在一条 TCP 连接上同时发出去。但也恶化了 TCP 的一个问题,队头阻塞,如图:
HTTP2 在一个 TCP 连接上同时发送 4 个 Stream。其中 Stream1 已经正确到达,并被应用层读取。但是Stream2 的第三个 tcp segment 丢失了,TCP 为了保证数据的可靠性,需要发送端重传第 3 个 segment 才能通知应用层读取接下去的数据,虽然这个时候 Stream3 和 Stream4 的全部数据已经到达了接收端,但都被阻塞住了。
不仅如此,由于 HTTP2 强制使用 TLS,还存在一个 TLS 协议层面的队头阻塞。
Record 是 TLS 协议处理的最小单位,最大不能超过 16K,一些服务器比如 Nginx 默认的大小就是 16K。由于一个 record 必须经过数据一致性校验才能进行加解密,所以一个 16K 的 record,就算丢了一个字节,也会导致已经接收到的 15.99K 数据无法处理,因为它不完整。
那 QUIC 多路复用为什么能避免上述问题呢?
-
QUIC 最基本的传输单元是 Packet,不会超过 MTU 的大小,整个加密和认证过程都是基于 Packet 的,不会跨越多个 Packet。这样就能避免 TLS 协议存在的队头阻塞。
-
Stream 之间相互独立,比如 Stream2 丢了一个 Pakcet,不会影响 Stream3 和 Stream4。不存在 TCP 队头阻塞。
当然,并不是所有的 QUIC 数据都不会受到队头阻塞的影响,比如 QUIC 当前也是使用 Hpack 压缩算法 ,由于算法的限制,丢失一个头部数据时,可能遇到队头阻塞。
总体来说,QUIC 在传输大量数据时,比如视频,受到队头阻塞的影响很小。
加密认证的报文
TCP 协议头部没有经过任何加密和认证,所以在传输过程中很容易被中间网络设备篡改,注入和窃听。比如修改序列号、滑动窗口。这些行为有可能是出于性能优化,也有可能是主动攻击。
但是 QUIC 的 packet 可以说是武装到了牙齿。除了个别报文比如 PUBLIC_RESET 和 CHLO,所有报文头部都是经过认证的,报文 Body 都是经过加密的。
这样只要对 QUIC 报文任何修改,接收端都能够及时发现,有效地降低了安全风险。
如下图所示,红色部分是 Stream Frame 的报文头部,有认证。绿色部分是报文内容,全部经过加密。
连接迁移
一条 TCP 连接是由四元组标识的(源 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 还能实现证书压缩,减少证书传输量,针对包头进行验证等。
相关阅读
摘要: QuickBI支持多种数据源连接,添加数据源是数据分析展示的第一步,下面来详细介绍一下产品数据源支持情况!Quick BI是一个基于云计
QuickCHM V2.6 安装过程下载地址:http://www.w856.com/post/46.html1. 安装(默认安装)一开始下载了一个QuickCHM V2.6 安装后直
苹果 MacPaint 和 QuickDraw 的源代码(2010)
价值 | 思考 |