作者:王莹, 腾讯应用开发 高级工程师
商业转载请联系腾讯 WeTest 获得授权,非商业转载请注明出处。
原文链接:http://wetest.qq.com/lab/view/384.html
你听过 HTTPS、HTTP2.0、SPDY,但是这些应用层协议都是基于可靠的传输层协议 TCP 来实现的。那么,基于高效的 UDP 协议有没有一种相对可靠的应用层协议呢?
你听过 HTTPS、HTTP2.0、SPDY,但是这些应用层协议都是基于可靠的传输层协议 TCP 来实现的。那么,基于高效的 UDP 协议有没有一种相对可靠的应用层协议呢?
图 1 why quic?
Quick UDP Internet Connection(QUIC) 协议是 Google 公司提出的基于 UDP 的高效可靠协议。
说它高效,是因为使用了无连接的 UDP 而不是迭代周期更长的需要修改系统内核网络栈的 TCP 协议。
说它可靠,是因为将改进了的可靠 TCP 的协议特征用到了 QUIC 上。
同时,也复用和改进了 HTTP2 的典型特征,譬如二进制分帧,多路复用,header 压缩等。
图 2 what's quic?
建立连接
出于 HTTP 的明文和无法验证服务器的真实性,在 TCP 的基础上引入了 TLS 协议,目前广泛使用的 HTTPS 是基于 TCP+TLS 协议,HTTP2 也被主流浏览器默认支持 TLS。
但对于建立连接的耗时而言,TCP 本身就需要握手时延,而 TLS 协议为了使得客户端和服务器端在不安全的网络通信中协商出后续安全通信所需的加密私钥,更是要经过额外 2 次 RTT(RoundTrip Time 往返时间)
图 3 TCP+TLS 建连过程
除了 TCP 建立连接过程,TLS 握手过程要经过如下步骤:
1、客户端提供加密套件(算法)列表,版本等信息
2、服务器端提供自己的证书,选择的加密套件,非对称加密公钥(自己保留私钥)等
3、客户端提供自己的证书,用服务器公钥和加密套件加密的自己的私钥
4、服务端用保留的私钥解密客户端传来的加密私钥,得到的私钥即为后续加密传输使用的对称密钥,最后完成握手
此时,双方协商出了对称密钥。基于 TCP+TLS 的 HTTP2 建连过程结束,大约需要耗时 200-300ms。
为了保证安全,QUIC 也是加密传输数据的,所以在 QUIC 的建连过程中也需要双方协商出一个加密私钥。但与 TLS 不同,QUIC 采用的加密算法仅需要一个 RTT 就能实现密钥交换,并且该算法也被用于目前正在草案阶段的 TLS1.3 协议。该就是 Diffie-Hellman 密钥交换算法。
图 4 Diffie-Hellman 算法
可以看到,客户端和服务端各自保留了自己的私钥 a 和 b,通过交换各自的公钥 B 和 A,以及基底 G 和很大的质数 P,双方就能计算出相等的私钥 S,这个 S 就是加密传输的对称密钥。
另外,根据离散对数的不可逆,即使拿到 G,P,和质数 B,也很难推导出私钥 b(同理私钥 a),也就保证了计算密钥的安全。
该过程对应到 QUIC 建连的过程中如下图。
图 5 1RTT 建连
1、客户端发起 Inchoate client hello
2、服务器返回 Rejection,包括密钥交换算法的公钥信息,算法信息,证书信息等被放到 server config 中传给客户端
3、客户端发起 client hello,包括客户端公钥信息
此时,双方各自计算出了对称密钥。QUIC 的 1RTT 建连过程结束,平均只耗时 100ms 以内。
后续发起连接的过程中,一旦客户端缓存或持久化了 server config,就可以复用并结合本地生成的私钥进行加密数据传输了,不需要再次握手,从而实现 0RTT 建立连接。
协商升级
一般情况下,Chrome 浏览器和服务器端协商使用 QUIC 协议要经过如下步骤:
1、客户端发出 tcp 请求
2、服务端如果支持 quic 可以通过响应头 alt-svc 告知客户端
3、客户端同时发起 tcp 连接和 quic 连接竞赛
4、一旦 quic 建立连接获胜则采用 quic 协议发送请求
5、如遇网络或服务器不支持 quic/udp,客户端标记 quic 为 broken
6、传输中的请求通过 tcp 重发
7、5min 后尝试重试 quic,下一次尝试增大到 10min
8、一旦再次成功采用 quic 并把 broken 标记取消
其中,支持 quic 的 alt-svc 头部信息如下图示,ma 为有效时间(单位秒),v 为支持的 quic 版本信息。
图 6 alt-svc 头信息
研究过程中发现,除了 alt-svc header,http2.0 下服务端还可以通过支持 alt-svc frame 来让客户端在第一次请求的时候就走新协议,比通过 header 让浏览器第二次才能请求新协议更高效,这个留给后续研究。
连接迁移
TCP 使用四元组(源 IP,源端口,目的 IP,目的端口)来标识一条连接,当四元组中的 IP 或端口任一个发生变化了连接就需要重新建立,从而不具备连接迁移的能力。
而 QUIC 使用了 connection id 对连接进行唯一标识。即使网络从 4G 变成了 wifi,只要两次连接中的 connection id 不变,并且客户端或者服务器能通过校验,就不需要重新建立连接,连接迁移就能成功。
改进的多路复用
在 SPDY 协议出现以前,每个 HTTP 请求都需要建立一条 TCP 连接,那么如果希望请求并行,就需要同时开启多条 TCP 连接(都是有建连代价的)。而大多数浏览器对于同一个域名可以建立的最大 TCP 连接数是有限制的,所以,如果超出限制,更多的请求资源是无法并行的。
SPDY 协议以来提出的多路复用,是让所有请求基于一条 TCP 连接,解决了上述的问题但同时引入了新的问题——队头阻塞,如果某个资源的某个包丢失了,因为 TCP 是保证时序的,就会在接收端形成队头阻塞,TCP 此时无法区分各个资源的包是否关联,因此会停止处理所有资源直到丢包恢复。
图 7 基于 TCP 的多路复用
QUIC 也有多路复用,但是 QUIC 是基于 UDP 的,UDP 不需要保证包的时序,只会在接收包的时候对包进行重组,因而不存在等待丢包恢复的队头阻塞问题,这样某个资源的包丢失只会影响自身不会影响到其他资源的继续传输,所以是改进的多路复用。
图 8 基于 QUIC 的多路复用
双级别流量控制
QUIC 是多路复用的,多条 stream 可以建立在一条 connection 上,所以 QUIC 的流量控制不仅基于单个 stream,还基于 connection。
stream 级别的流控能够控制单 stream 的数据发送情况。另外,接收窗口的收缩取决于最大接收字节的偏移而不是所有已接受字节的总和,它不像 tcp 流控,不会受到丢失数据的影响。
图 9 stream 流控
如果满足 (flow control receive offset - consumed bytes) < (max receive window / 2)
会触发 WINDOW_UPDATE frame 的发送来增大发送窗口大小。
图 10 WINDOW_UPDATE 触发前
图 11 WINDOW_UPDATE 触发后
connection 级别流控算法和 stream 一致,各项数值是所有 stream 的总和。
connection 级别的流控存在的必要是,即使做好了 stream 流控,但如果 stream 过多也会导致 connection 过度消耗带宽和系统资源;而且即使某一条 stream 过慢,其他 stream 依然能触发
connection 级别的 WINDOW_UPDATE,从而不会被影响。
图 12 connection 流控
拥塞控制
我们知道 TCP 有多种拥塞控制算法,当遇到网络拥塞会通过减包等方式来避免网络环境恶化。但是,UDP 本身是没有拥塞控制的,一旦不加约束的使用会导致侵占其他 “守规矩” 的网络协议的带宽。
所以,为了避免上述情况,基于 UDP 的 QUIC 协议借鉴了 TCP 的一些优秀的拥塞控制算法,如默认使用 Cubic,同时,为了避免 AIMD 机制带来的带宽利用率低,采用了 packet pacing 来探测网络带宽。
思路是,QUIC 会通过追踪包的到达时间来预测当前带宽的使用情况,以决定是否提高,保持或者减少发送包的速率来避免网络拥塞。
图 13 packet pacing
丢包恢复
类似拥塞控制,除了基于 TCP 的一些丢包恢复机制,如:TLP,FACK。QUIC 的丢包恢复也在一些方面做了改进。
比如:通过引入严格递增的 sequence number 使得计算 RTT 更加精确。更精确的 RTT 也意味更精确的 RTO 和超时重传机制。
还比如我们知道 TCP 中有个 SACK 选项,该选项打开时用于记录传输过程中一些没有被确认的数据的范围,便于后续定向重传多组丢失数据,而不是全部重传,所以更多的范围便于更多的选择重传,也意味着更少的重传包频率。但 TCP 最多支持 3 个 SACK 范围,而 QUIC 能支持 255 个。
除了上述基于 TCP 的改进的丢包恢复特性以外,早期的 QUIC 版本还有一个丢包恢复机制,就是 FEC(Forward Error Correction),这个特性虽然目前处于正在改造阶段(可能会浪费带宽并且作用不是很明显),但是依然是一个有意思的解决方案。FEC 的思路是通过在一组包(一般是 10 个)中,通过增加一个 FEC 包,并用 FEC 和每个包进行 XOR,如果一旦有丢包,那么将 FEC 包和其余包 XOR,得到的 FEC 包就是那个丢包,所以一组包最多只能恢复一个丢包。
更多特性
除了上述的主要特性,QUIC 还有一些其他特性,如:
● 通过 header stream 保证流顺序
● 底层保证连接持久
● 源地址令牌防止地址欺骗
● 握手时压缩证书避免放大攻击
在此不深入研究,大家有兴趣可以翻阅 Google 相关的文档查阅。
● Google 超过 50% 的请求来自 QUIC
● 目前 Youtube 有 20% 的流量来自 QUIC
● 微博移动端全面支持 QUIC 协议
● 客户端
最新版本 PC Chrome(控制开启/关闭 quic)
● 服务器
经 stgw 改造支持 quic 的 nginx
● 页面地址(机器被回收,后续会更换机器供测试)
https://stgwquic.kof.qq.com/club/platform/act/gift/test1.html
https://stgwquic.kof.qq.com/club/platform/act/gift/test2.html
https://stgwquic.kof.qq.com/club/platform/act/gift/test3.html
● 网络
公司 staffwifi
● 抓包工具
wireshark
● 效果对比
图 15 HTTP1.1 协议的页面
图 16 HTTP2 协议的页面
图 17 QUIC 协议的页面
图 18 页面请求 20 个,大小 2MB
图 19 页面请求 15 个,大小 465KB
● 客户端支持
X5 内核团队(移动端)
依赖用户浏览器支持 QUIC 情况(PC 端)
● 服务端支持
STGW 团队
● 业务自身
按照路径灰度,控制灰度策略
以上测试 demo 数据是基于公司良好的网络情况下测试得到的,在实际运用过程中,大家可能更关心在复杂的网络环境下 QUIC 的表现。于是 QQ 会员团队通过灰度现网的一个页面来考察 QUIC 在现网的性能情况。
● 页面情况
Android 日 PV100w,页面大小 95KB
总请求 30 个,其中主资源请求 1 个,CDN 请求 24 个,其他请求 5 个
展示部分依赖 php 直出和 js 渲染
● 灰度情况
QUIC 请求 1 个(php 页面主资源),HTTP2 请求 29 个
● 灰度策略
客户端每天放量,对比灰度过程中页面主资源的 HTTP2 和 QUIC 的性能数据
● 灰度效果
图 20 QQ 会员页面 QUIC 灰度情况
● 效果说明
因为建连依赖于 1RTT 和 0RTT 机制,使得 QUIC 建连平均耗时仅需 46ms,比 HTTP2 的 225ms 减少 180ms 左右。
由于目前灰度量只占到总请求量的 10%,因此更严谨的性能对比数据有待进一步提高灰度范围,以上仅作现阶段参考。
但依然可以看到 QUIC 在现网环境总体表现忧于 HTTP2。
在实践 QUIC 的过程中,我们也遇到了一些需要注意的问题。
● QUIC 支持头部 alt-svc 的缓存机制面向整个域名
业务即使只在一个页面路径下加了支持头部(STGW 可以是路径级别支持,X5 只能是域名级别支持),浏览器也会根据头部的缓存时长作用于用户访问该域名下的其他页面,但 STGW 可能只支持了路径级别。所以灰度过程中,尽量使用有独立域名的页面。从而保证尽量不影响其他页面的请求情况(虽然 QUIC 请求失败会降级 H2),尽量减少 ma 缓存时间。
● 客户端对于 QUIC 的协商机制有待改善
在 X5 目前的实现机制中,无论如何首次请求都会基于 HTTP2,后续才会尝试 QUIC,但如果缓存时间设置的不够长(譬如 1 天),会使得用户一般 1 天内很难通过再次请求走到 QUIC。所以,目前我们做的是推动 X5 在请求时候让白名单链接直接尝试 QUIC。同时,避免缓存时间随着用户退出手 Q 而失效,推动让其落地。
● 目前客户端基于 X5 的 QUIC 与一些基于缓存和预加载的页面秒开方案冲突
如果你们的页面是基于 X5 内核,但是使用了上述类似的技术,那么存在一个问题是原本直接通过 X5 走 QUIC 协议的页面不直接走 X5 了,而是基于你当前方案的缓存或者自定义的 webview 请求方式。于是通过统计数据会发现 QUIC 的请求量很少,因为上述技术目前还不支持 QUIC 协议。当前的做法是在 QUIC 和该方案中二选一。
● CDN 请求灰度需要自支持
由于 CDN 请求不基于 STGW 代理,因而需要 CDN 团队针对业务方域名灰度支持,目前 CDN 整体处于运营商灰度 QUIC 阶段。在此之前如果需要灰度 CDN 请求就需要业务方自己处理转发。
● 运营商的 Qos 影响需要更完善的上报数据进行评估
使用 QUIC 协议比较担心的一个问题就是在网络质量差的情况下运行商 Qos 会对其产生怎样的影响。从目前整体的统计数据,包括慢速用户占比等情况来看,影响不是很大。后续需要推动各方完善上报监控该情况下耗时。
● 和 X5 团队一起解决首次请求必走 HTTP2 请求问题
● 和 X5 团队一起解决 alt-svc 缓存有效期落地问题
● 和终端团队一起解决秒开方案下 Webview 对 QUIC 的支持,争取既能使用 QUIC 协议又能使用秒开方案
● 配合 CDN 团队验证 QUIC 扩大灰度的支持效果
● 推动各方更细粒度的数据统计完善
https://docs.google.com/document/d/1gY9-YNDNAB1eip-RTPbqphgySwSNSDHLq9D5Bty4FSU/edit
QUIC Crypto Adam Langley Wan-Teh Chang
https://segmentfault.com/a/1190000007060218
https://segmentfault.com/a/1190000007075961
https://blog.csdn.net/xiaofei0859/article/details/77512746
http://fasterdata.es.net/host-tuning/packet-pacing/
https://imququ.com/post/http-alt-svc.html
HTTP over UDP: an Experimental Investigation of QUIC
QUIC FEC v1 Author: ianswett@google.com Last Updated: 2016-02-19
Understanding QUIC wire protocol
IETF93 QUIC BarBoF: Congestion Control and Loss Recovery
QUIC: Performance and Security at the Transport Layer
WeTest 压测大师——为了帮助开发者发现服务器端的性能瓶颈,腾讯 WeTest 开放了压力测试功能,通过基于真实业务场景和用户行为进行压力测试,实现针对性的性能调优,降低服务器采购和维护成本。
目前压测大师服务了包括王者荣耀、QQ 飞车手游、QQ 炫舞手游等多款高星级手游, 也服务了 QQ、NOW 直播、摩拜单车、企鹅 FM 等明星产品。
目前 WeTest 压测大师对外开放中,点击链接:http://wetest.qq.com/gaps 即可使用。
如果对使用当中有任何疑问,欢迎联系腾讯 WeTest 企业 QQ:800024531
腾讯 WeTest 有奖征文活动进行中,欢迎投稿!了解详情:
http://wetest.qq.com/lab/view/379.html