QUIC协议

  |   0 评论   |   0 浏览

背景

Cronet是一个网络库,易用、高性能、兼容标准、安全。它从Chromium而来,用于Android和ios平台。

Cronet库支持HTTP协议和QUIC协议。

原理

为什么要使用QUIC

[3]

QUIC协议时提到了几个关键特性:

  • 链接耗时更短
  • 拥塞控制更出色
  • 更好的多路复用
  • 前向纠错特性
  • 链接迁移特性

建立连接:链接耗时更短

先让我们来看看对于HTTPS页面,需要花在连接阶段的耗时。

step1. TCP三次握手,花费1RTT。

step2. TLS握手。TLS完全握手耗费2RTT,简化握手需要1RTT。

TCP是面向连接的协议,通讯前需要通过握手建立连接。TLS握手的主要目的在于双方约定一个对称秘钥,用于之后的数据传输。 传输秘钥十分重要,直接决定了后续传输的安全性,因此约定过程用到RSA等非对称加密来保证安全性,而且为了实现真随机设定了三次握手用来传递三个随机数,等到客户端和服务器都有这三个随机数后可以用相同的算法生成同一个对称加密秘钥。

因此HTTPS需要耗费2-3个RTT用来建立连接。而QUIC只用耗费0-1RTT的连接耗时,就能做到等同于TCP+TLS连接的效果,0RTT就等同于发给服务器的第一个包就带有请求数据,你会不会好奇怎么做到不浪费一个RTT耗时,就能建立安全的链接?

首先 UDP是不面向连接的,事先不需要握手过程就可以直接传输数据;

其次 我们之前提到过,HTTPS的TLS握手的一个主要目的在于约定一个对称秘钥,用于之后的数据传输。QUIC在这一步QUIC用了经典的秘钥加密算法(DH算法)来实现对称秘钥的交换,算法原理见下图,算法原理有兴趣的同学可以查阅资料。其巧妙之处在于,在算出对称秘钥的三个因子中,有一个因子是本地随机产生的,其不用参与网络传输,这就断绝了被第三方劫持的可能性。 如果pg是大数,那么即使全部截获到了pgAB,要破解出ab也是难的。而且秘钥的随机性是服务器和客户端共同保证的,保证了真随机性。

QUIC握手过程

QUIC首次连接需要1RTT,具体过程如下:

step1: 客户端发送inchoate Client Hello消息(CHLO)请求建立连接,服务器生成一组质数p以及其原根g。然后根据pg和随机生成数a算出A,将Apg放在serverConfig里面,发到Rejection消息(REJ)到客户端;

setp2: 客户端随机生成一个数b,并根据serverConfig里面的pA和b就可以算出初始密钥K ,并将B和用初始密钥加密的Data数据发到(full client hello消息)服务器。

step3: 服务器收到客户端的数据,用客户端发来的公开数B+server config生成同样的秘钥,来解密客户端发来的数据。然后发送Server hello消息(SHLO),这时会带上自己生成的服务器公开数 (服务器这是在更新自己的私钥,实际上这就是为了保证前向安全性 );

step4: 客户端收到这个服务器公开数后,二者便更新了秘钥,用SHA-256等算法推导出会话密钥 。以后就用这个会话秘钥通讯了。

后续的连接,如果客户端本地的serverConfig没过期(包含了Apg和其他前次协商信息),直接可以计算出初始秘钥K并加密传输数据,实现0RTT握手。

队头阻塞&流量控制

队头阻塞问题

既然TCP握手连接代价这么大,因此为了更好的利用已经建立好的连接,减少连接耗时,http1.1协议通过长连接方式让多个同 域名下的请求复用同一连接 ,但是必须排队使用。B请求需要等到A请求完成后进行,这样就可能导致队头阻塞现象(Head-of-line blocking),A请求不完成,后面的请求BCD都会被delay住而无法发起。

谷歌提出的SPDY协议的多路复用机制一定程度上缓解了队头阻塞现象,SPDY规定,同一链路上的请求不再依次等待 ,大家可以间隔进行,于是可能存在一条链路上依次有A->C->B->A的序列,你看B请求不用再等待A,C请求也不用等待B,队头阻塞不就解决了么?

其实并没有 ,无论HTTP2(HTTP/2 over TCP)、HTTP1.x协议还是SPDY协议都基于TCP协议,上层可以解决HOL问题,但是传输层的HOL依然存在。说起传输层TCP协议的队头阻塞,这要从TCP的流量控制策略说起了。

TCP的流量控制策略

TCP保证了数据的有序和可达性,所以原则上是数据按照序号依次发送和接收,下一个包的发送需要等到上一个包Ack到达。这样的话,在相邻两个包的发送间隙存在很长时间的空闲等待,好在TCP采用了滑动窗口机制 来减少了排队等待时间,双方约定一定大小的窗口,在这个窗口内的包都可以同步发送,接收者收到一个packet时会回复Ack给发送者,发送者收到Ack后移动发送窗口,发送后续数据。

但是如果某个packet丢失或者其对应的Ack包丢失,同样会出现一方不必要的等待。如下图情况,packet 5的Ack包丢失,导致发送端无法移动发送窗口,但接收者已经在等待后面的包了。必须等到接收者超时重传这个确认包,服务器收到这个ACK包后,发送窗口才会移动,继续后面的发送行为。

同时,对于已经被接收者接收的多个packets(比如B->A->C请求的三个包),在TCP层面是无法区分这三个包对应于上层的哪三个请求的,因此如果B包出现缺失,会导致后续已经被接收的包无法被应用层读取,请求A、C的包被B包阻塞。

QUIC的流量控制策略

UDP没有流量控制机制,由于QUIC的多路复用机制(QUIC上同一域名的多条请求可以走同一个链接),其流量控制分为Stream和Connection两种级别 的控制。这里Connection就是QUIC握手后建立的连接,Stream可以理解为链路上的请求。

首先,对于StreamA而言,起始的接收窗口就是其最大接收窗口(max receive window),随着接收者接收到一部分数据后,接收窗口变小。而接收到的数据中,有一部分已经读取(图中黄色部分),这部分数据的量到达一定的阈值后,就需要更新接收窗口并告知发送者。QUIC将这个阈值定义为:

update when (flow control receive offset - consumed bytes) < (max receive window / 2);

也就是当已经读取的数据大于最大接收窗口的一半时 ,发送WINDOW_UPDATE帧告诉发送者,接收窗口已经更新。比如告诉发送者,窗口后移了Bytes consumes字节。

而对于connection级别的流量窗口,其接收窗口大小就是各个stream接收窗口大小之和。

QUIC的流量控制机制,使得其相对于其他协议,队头阻塞问题解决得更加彻底:

  1. QUIC协议中同一个Stream内,滑动窗口的移动仅取决于接收到的最大字节偏移 (尽管期间可能有部分数据未被接收),而对于TCP而言,窗口滑动必须保证此前的包都有序的接收到了,其中一个packet丢失就会导致窗口等待。
  2. QUIC中同一个Connection内,不同Stream互相独立 ,队头的Stream A被阻塞后,不妨碍StreamB、C的读取。而对于TCP而言,其不知道将不同的Stream交给上层哪一个请求,因此同一个Connection内,Stream A被阻塞后,StreamB、C必须等待。

拥塞控制

目前QUIC默认采用Cubic拥塞控制算法来实现拥塞控制,就这点来看和TCP采用的是一套机制(譬如我们熟知的慢开始、拥塞避免、快重传、快恢复策略),但QUIC的这套拥塞控制策略还是与TCP有一些明显的区别和改进:

- 灵活性

TCP协议内置在系统协议栈层面,你想要改变其拥塞控制策略,需要在系统层面进行修改。而QUIC基于UDP,当你想要改变拥塞控制算法,或者进行调优工作,只需要在应用层进行修改,这并不是一件困难的事情,而且对其他应用没有影响。目前谷歌提供了两套算法(Cubic和NewReno)以供选择,并提供了一套很灵活友好的接口,让你去实验新的拥塞控制算法,而且还可以为不同应用设定不同的拥塞控制策略。

- 提供更为详细的信息

QUIC提供了更加详细而准确的信息。如其严格单调递增的Packet序列 ,能够很容易的区分packet是来自重传还是首次传输,避免了重传模糊。再如QUIC携带有关收到数据包和发送ACK之间延迟的信息,可以更准确的计算RTT时间。

另外,相对于TCp的SACK(Selective Acknowledgments,用来标注哪些包已经收到了让对方更方便的选择性重传),QUIC还提供了更大范围(0-256)的NACK(Negative Acknowledgments,用来标注哪些包没有收到),来帮助发送者快速的重传丢失包。

- 尽可能避免超时重传

为了尽可能的实现快重传而不是超时重传,QUIC采用了Tail Loss Probes (TLPs)实现某些情况下的快重传机制触发。

TLP算法如下图,服务器的segments 6-10丢失,客户端在等待s6时,由于没有收到后续的序列,因此无法触发快速重传机制,时间达到probe阈值(PTO)后,TLP算法对segments10进行重传,客户端收到这个重传序列,就能触发快速重传机制。而QUIC会在PTO之前就发送两个TLPs来尽可能避免等到超时再重传。

QUIC的其他特性

前向纠错&重传

数据包丢失不仅导致重传耗时,还会使拥塞窗口变小,从而降低吞吐量,影响了数据传输速度。谷歌在QUIC早期采用了前向纠错码(Forward-Error Correction)来减少包丢失现象。在一个Group中带上一个额外的fec包(N+1个包),分组中任意一个包丢失,都可以用其余N个包来恢复。这种做法同时也带来了冗余,可能没10个包中就要额外传输1个冗余包,在网络状况好的时候是一种浪费,目前谷歌已经废弃了QUIC的FEC功能。

同时,对于一些非常重要的包,QUIC在发送后的短时间内如果没收到回包,便会重发请求,以确保重要的节点不被Delay。因此抓包时你可能会看到有5个CHLO包在100ms内相继被发送。

链接迁移

当客户端IP或端口发生变化时(这在移动端比较常见),TCP连接基于两端的ip:port四元组标示,因此会重新握手,而UDP不面向连接,不用握手。其上层的QUIC链路由于使用了64位Connection id作为唯一标识,四元组变化不影响链路的标示,也不用重新握手。因此网络链接状态变化时不会增加额外的握手重连耗时。

QUIC在外网环境下的表现

腾讯浏览服务上半年对部分合作方开放QUIC服务,重点接入了手机QQ会员业务和黄钻业务,这两个业务目前的日均QUIC访问量都在百万级别。我们从性能和异常监控两个维度来考察QUIC的外网表现。

QUIC性能表现

上图是黄钻业务下走QUIC和HTTP2的性能数据对比(取6月15-17日三天大盘上报的平均数据)。主资源拉取耗时平均减少近100ms,首屏和Pagefinish耗时减少约20%。

QUIC监控数据

QUIC对于其他协议有速度上的优势,而对业务来说稳定是首要的。腾讯浏览服务在QUIC场景搭建了完善的异常上报机制,对X5内核的QUIC业务进行监控。下面一起来看看QUIC的外网监控表现情况:

  • QUIC成功率监控

这一维度主要监控QUIC的连接成功率以及竞速成功率,上图为黄钻业务06月15-17号三天成功率及竞速成功率数据,目前QUIC的连接成功率保持在98%以上,QUIC的竞速成功率在70%以上。

  • QUIC失败监控

对于QUIC失败异常,X5内核提供了详细的QUIC细分错误码以分析错误原因,从上报的大盘数据来看:

  • QUIC链接失败率不到2% ,连接失败的情况,以QUIC连接超时为主 。这种情况一般由于地区的QoS策略导致。
  • QUIC失败率较高的三个省份为:贵州、广西和新疆;失败率较高的运营商为:教育网和长城宽带。

QUIC在弱网络环境下的表现

QUIC连接时的0RTT特性,以及在传输过程中对TCP协议缺陷的改进(如改进的流量控制和拥塞控制),让QUIC在弱网络下可能取得更大的速度优势。我们使用腾讯企业云的网络模拟平台模拟弱网络环境,对比QUIC和HTTP2在不同的网络丢包率下,QQ会员主页的html主资源(约60kb)加载速度。

ConnectWaitRecvTotal
QUIC-0RTT53.0126.496.6276.0
QUIC-1RTT126.7142.088.3356.9
HTTP2165.3140.8106.5412.5

在WIFI网络、无丢包情况下,主资源的连接、等待、接收耗时对比如上表格所示。QUIC在连接及接收流程耗时较少,这与我们观察到的现网数据相吻合。

0%5%10%20%40%
QUIC 0RTT276.0291.4366.0527.71274.3
QUIC 1RTT356.9412.9533.2790.52670.4
HTTP2412.5506.9668.51380.9-

表格中列出了在不同网络丢包率下,QQ会员主资源加载耗时的对比数据。QUIC的优势随着丢包率的增大而更加明显。HTTP2在40%丢包率时开始出现页面加载不出来的现象,而QUIC还能完成请求任务。下图为随着丢包率上升,QUIC相对于HTTP的性能提升率趋势,在20%丢包率下,QUIC-1RTT下和QUIC-0RTT下请求页面主资源的耗时分别节省了约43%和62%。

Cronet库协议选择

[1]

Cronet能够同时Http协议和QUIC协议,那么他是如何做出选择使用哪种协议进行通信的呢?

首次请求

  • 1、首次请求,客户端发送正常的HTTP协议请求。此过程与正常HTTP请求的唯一区别是,携带了支持QUIC协议的信息,告诉Server端,Client支持QUIC请求
  • 2、Server端收到请求后,判断自身是否支持QUIC协议
  • 3-1、如果Server端支持QUIC协议,Server端直接在response header中添加一个响应头: alt-syc
    alt-syc携带的是什么信息呢?主要包含三部分信息
    • quic=ip:port 告诉客户端QUIC建连的IP和端口
    • ma=xxxxx 主要用来标识服务是否可用(是否在有效期)
    • v=xx,xx 标识Server端支持的QUIC版本
  • 3-2、如果Server端不支持QUIC协议,则直接按照HTTP协议返回,response header中不包含:alt-syc头部信息。

第二次请求

  • 1、客户端第二次请求,Cronet网络库同时发出基于TCP的HTTP连接和基于UDP的QUIC连接
    • TCP连接基于正常的DNS解析进行建连
    • QUIC连接是基于第一次请求时Server端在alt-syc头信息中标注的ServerIP和端口号进行发起的,而不走DNS解析
  • 2、Server接收两种连接后均会做出反馈给客户端
  • 3、客户端等待两个连接的响应,Cronet会按照时间维度判断哪个连接首先建立成功。
    • 如果QUIC连接首先完成,则后续请求均走此QUIC连接;
    • 如果HTTP连接首先完成,则后续直接按照HTTP连接与Server通信;

总结

看完这两个过程,不难发现,Cronet创建QUIC连接时,其实是不依赖于本地DNS解析的,正常需要建立QUIC连接的IP和端口均是有Server端提前告知的,因此Cronet对HTTP和QUIC协议的选择可以归纳为以下几点:

  • 首次请求,基于DNS建立HTTP连接,并下发QUIC的建连信息
  • 第二次请求,采用竞速模式,以时间维度则其优。
  • QUIC建连时,不依赖本地DNS解析,而是按照Server下发信息进行建连。

弱网

弱网指标

[2]

指标的具体数据可以借助一些网络测试工具进行模拟,比如腾讯的WeTest, 苹果的 Network Link Conditioner,Facebook 的 ATC(Augmented Traffic Control),来获取不同场景的指标数据。当然最终还是在发布产品中建立上报体系监控实际运行数据。

  1. httprtt(http Round-Trip Time)又名 TTFB(Time to first byte),指从客户端请求的第一个字节开始发送到接收到 http header 的第一个字节的时间差
  2. tcprtt(tcp Round-Trip Time)指客户端 tcp 信道第一个字节发送到接收第一个字节的时间差
  3. throughput,中文名字吞吐量,它是用来衡量单位时间内成功传送数据的数量,是可以比较客观的衡量网络质量的指标 。吞吐量 =(获 bits 结束大小 - 获 bits 开始大小)/(获 bits 结束时间 - 获 bits 开始时间)。
  4. signal strength,这里指的是无线信号强度
  5. bandwidth-delay product,中文名带宽时延乘积指的是一个数据链路的能力(throughput)与来回通信延迟(rtt)的乘积
  6. ICMP rtt,也就是ping耗时

弱网优化

一次网络请求大致过程是:(解析代理)->DNS解析Host->建立连接->通过协议传输->后台或CDN逻辑->数据返回->结束连接

终端优化

  1. DNS优化。包括接入大厂的HTTP(S)DNS,访问最近的DNS服务器;内置DNS服务IP列表;缓存DNS;依次并发多DNS请求,取最快的一个(腾讯mars复合连接策略)。

ref: https://juejin.im/post/5cbfda...

  1. 预连接/连接复用。包括Http/TCP的keep-alive,减少Http/TCP建立连接的耗时。
  2. 多级缓存。Http Cache、应用层Cache。
  3. 请求优先级。合理调度并发数量。
  4. HTTPS SSL session持久化以及复用。减少SSL握手时间。

协议优化

  1. QUIC(Quick UDP Internet Connections)基于UDP实现传输应用层协议。优点有减少握手次数、解决TCP丢包时队头拥塞问题、连接迁移等,是Http 3.0标准协议的核心。

因暂无时间配置个人服务器,而且业务短期内也不会切QUIC,所以这里引用百度的优化数据,也有其他开发者资料显示弱网下UDP丢包更严重。

弱网优化的收益我们主要从上面讲到的进入弱网状态后的手段来看,包括开启 QUIC,QUIC 预连接,开启复合连接。
1)弱网下开启 QUIC 后,网络连接成功率提升 0.01%,平均耗时降低 23.5%。
2)弱网下开启 QUIC 预连接后,QUIC 协议的 pv 从 37 万涨到 90 万。
3)弱网下开启复合连接后,bad 状态下耗时降低 2.5%,offline 状态下耗时降低 7.7%。
ref:https://www.infoq.cn/article/...*DsymqbGvy

  1. 请求合并。包括请求域名的合并,减少DNS查询时间;包括请求次数的合并,例如GraphQL。
  2. 心跳长连接,用于消息push等及时下发。微信Mars就是通过定时发送心跳包跟后台建立消息通道。

ref:https://juejin.im/post/5c6234...

后台优化

  1. 数据量优化。包括压缩数据、增量下发、压缩请求头、304检查。
  2. 服务器部署区域。

Cronet功能

1.支持比较全面的网络状态监控,包括http/tcp rtt、吞吐量、信号强度、系统网络状态变化。通过ExperimentalCronetEngine.enableNetworkQualityEstimator开启。
2.基本支持上述的终端优化。
3.支持HTTPS/SPDY/QUIC等协议。

钉钉弱网优化

作为5亿级用户规模的企业级产品,钉钉一直专注于用户体验和技术创新,从可用到易用到好用,对新技术保持开放的态度。

今年上半年,钉钉终端团队发起了多个基础体验专项,如秒开专项、高铁专项、低功耗模式等。手机续航一直是手机厂商和用户非常关心的问题,钉钉在Android应用的功耗上进行了优化,针对消息非常多的重度用户,开发了低功耗模式。用户开启该模式后,钉钉功耗使用可降低70%以上,目前已经有超过200万用户开启。

同样在高铁专项中,钉钉移动端采用了多网卡、QUIC协议、渐进式连接等技术优化策略,将弱网的请求成功率提升了20%、弱网请求耗时降低了40%,使用户在高铁、地铁、电梯、车库等弱网环境下依然能流畅使用。

参考

  1. Cronet网络协议选择之HTTP2与QUIC的竞速
  2. Cronet网络库系列(四):移动网络弱网优化综述
  3. 天下武功,唯'QUICK'不破,揭秘QUIC的五大特性及外网表现
  4. 钉钉文档、会议首批适配荣耀信任环技术