如果你的 App,在不需要任何修改的情况下就能提升 15% 以上的访问速度。特别是弱网络的时候能够提升 20% 以上的访问速度。
如果你的 App,在频繁切换 4G 和 WIFI 网络的情况下,不会断线,不需要重连,用户无任何感知。如果你的 App,既需要 TLS 的安全,也想实现多路复用的强大。
如果你刚刚才听说 HTTP2 是下一代互联网协议,如果你刚刚才关注到 TLS1.3 是一个革命性具有里程碑意义的协议,但是这两个协议却一直在被另一个更新兴的协议所影响和挑战。
如果这个新兴的协议,它的名字就叫做“快”,并且正在标准化为新一代的互联网传输协议。
你愿意花一点点时间了解这个协议吗?你愿意投入精力去研究这个协议吗?你愿意全力推动业务来使用这个协议吗?
QUIC 在腾讯的实践
腾讯安全云网关 (STGW) 和腾讯云负载均衡器(Cloud Load Balance)在 2017 年 7 月份就已经在服务端上支持了 Quic 协议,在工程实现上也有很多优化点,同时在生产环境中也取得了较好的效果。相比现在几个开源的方案,STGW 的实现主要有如下几个优点:
高性能。
复用 Nginx 全异步事件驱动框架。
私钥代理计算集群加速签名计算。
全局缓存提速,减少计算量的同时,提升访问速度。
强大的功能。
支持 Nginx 现有全部模块指令,丰富的第三方模块。
复用 Nginx 模块框架,非常灵活地新增第三方功能。
稳定性。
代码完全自主可控。
正在经受腾讯亿万级并发流量的考验。
同时我们也在腾讯很多业务包括 QQ 空间、WEB 游戏页面、腾讯云 CLB 上灰度支持了 QUIC 协议。详细的收益数据可以参考第 6 章。
QUIC 线下测试方案
在决定使用 QUIC 协议之前,我们需要对 QUIC 协议的特性及性能做一个全面的测试,如何测试呢?这里简单说一下测试方案。
需要特别说明的测试是在 2016 年底进行的,目前所有域名已经失效,无法再进行测试。
页面构造
根据 httparchive.org 的统计,构造了如下页面:
测试环境
手机:华为 mate9 User-Agent:MHA-AL00 Build/HUAWEIMHA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36 操作系统:Android 7.0 服务端 QUIC 程序:caddy 0.9.4
网站和客户端分布如下:
QUIC 测试框架
①表示客户端主动发起的用户请求。
②表示从 html 里发出的资源请求。
③表示数据上报请求。
测试流程
整个测试流程通过 python 脚本和 adb shell 工具自动化进行。其中移动端和 PC 端的控制流程有所区别。分别简介如下:
移动端测试流程
准备事项:
打开手机的 usb 调试选项。
在 PC 端安装 adb。
在 PC 上通过 USB 连接手机,确保能够通过 adb devices 命令发现设备。
在服务端配置 cache-control: no-cache, no-store。禁止客户端缓存。
自动化测试流程如下:
启动 android chrome。
访问 https://www.helloworlds.cc。
待页面加载完后会触发 onload 事件,将各个时间点上报。
PC 端流程
PC 端不需要 adb,使用 webbrowser 模块控制 chrome 完成即可。
测试结论
由于公司内网的 WIFI 环境不稳定,多次测试发现数据跳动较大,4G 环境下的数据更加稳定可靠,所以主要结论参考 4G 网络下的数据。
QUIC 的优势非常明显,即使在元素比较少(12 个元素)的情况下,相比 HTTP 也能提升 9%,相比 HTTP2 提升 42%,相比 HTTPS 提升 52%。
在页面元素增多的情况下,QUIC 的优势就更加明显,相比 HTTP 提升 36%,相比 HTTP2 提升 47%,相比 HTTPS 提升 64%。
QUIC 性能优化
QUIC 的特性虽然比较先进,但是实现起来却非常复杂,在工程实现方面也有很多优化的空间。比如如何提升 0RTT 成功率,减少服务端的 CPU 消耗量,实现连接迁移和动态的拥塞控制算法等。
提升 0RTT 成功率
安全传输层虽然能够实现 0RTT,优势非常明显。但问题是,不是每一次连接都能实现 0RTT,对于我们的客户端和服务端来讲,如何最大程度地提升 0RTT 的成功率?
0RTT 能实现的关键是 ServerConfig。就像 TLS session resume 实现的关键是 session id 或者 session ticket 一样。
ServerConfig 到达服务端后,我们根据 ServerConfig ID 查找本地内存,如果找到了,即认为这个数据是可信的,能够完成 0RTT 握手。
但是会有两个问题:
进程间 ID 数据无法共享。
多台服务器间的 ID 数据无法共享。
明确了问题,那工程层面就需要实现多进程共享及分布式多集群的 ID 共享。
SeverConfig Cache 集群
Stgw 在生成 ServerConfig ID 和内容时,会存储到全局的 Cache 集群。用户握手请求落到任意一台 STGW 机器,从全局 Cache 集群都能找到相应的内容,实现 0RTT 握手。
加密性能的优化 签名计算
QUIC 实现 0RTT 的前提是 ServerConfig 这个内容签名和校验都没有问题。由于 ServerConfig 涉及到 RSA 签名或者 ECDSA 签名,非常消耗我们的 CPU 资源。根据之前的测试数据,RSA 私钥签名计算会降低 90% 的性能。
那如何优化呢?使用 RSA 或者 ECDSA 异步代理计算。核心思路也是三点:
算法分离。剥离私钥计算部分,不让这个过程占用本地 CPU 资源。
异步执行。算法剥离和执行异步的,上层服务不需要同步等待这个计算过程的完成。
并行计算。我们使用配置了专用硬件的私钥计算集群来完成私钥计算。
架构如下图所示:
签名代理计算
对称加密的优化
相比非对称密钥交换算法来讲,对称加密算法的性能非常卓越(好 1 到 2 个数量级),但是如果应用层传输内容较大的话,特别是移动端的 CPU 计算能力较弱,对称加密算法对性能的影响也不容忽视。
表格 1 常用对称加密算法性能比较
如何优化呢?通过异步代理的方式显然不可能。原因是:
会极大降低用户访问速度。由于应用层的每一个字节都需要对称加解密,使用异步的方式实现会严重降低加解密的实时性。
那有没有同步的优化方式呢?有。类似 SSL 硬件加速卡,intel 针对 AES 算法实现硬件加速,并将它集成到了 CPU 指令里。
AES-NI 指令
AES-NI 是 intel 推出的针对 AES 对称加密算法进行优化的一系列指令,通过硬件计算实现计算速度的提升。
如何测试 AES-NI 的性能呢?
通过环境变量
aes-ni: OPENSSL_ia32cap="~0x200000200000000" openssl speed -elapsed -evp aes-128-gcm 或者在代码里将 crypto/evp/e_aes.c # define AESNI_CAPABLE (OPENSSL_ia32cap_P[1]&(1<<(57-32))) 进行设置。
aesni 对性能的提升约 20%, 由 4.3W 提升到 5.1W。
这里需要注意的是,如果需要单独使用 openssl 的 API 进行 AES 对称加解密,最好使用 aes evp API,这样才会默认开启 AES-NI 指令。
chacha20-poly1305
chacha20-poly1305 是由 Dan Bernstein 发明,并且由 google 推出的一种带身份认证的对称加密算法。其中 chacha20 是指对称加密算法,poly1305 指身份认证算法。这个算法是对没有 AES 硬件加速功能的移动平台的补充,比如 ARM 芯片。
从 google 公布的数据来看,chacha20-poly1305 能够提升 30% 以上的加解密性能,节省移动端耗电量。当然,如果手机端支持 AES-NI 指令的话,chacha20 就没有优势了。
Openssl 在 1.1.0 版本中正式支持了 chacha20-poly1305。
连接迁移 (Connection Migration) 的实现
那 STGW 服务端如何实现的呢?我们在 CLB 四层转发层面实现了根据 ID 进行哈希的负载均衡算法,保证将相同 ID 的 QUIC 请求落到相同的 CLB7 层集群上,在 CLB7 上,我们又会优先根据 ID 进行处理。
图示如下:
QUIC 连接迁移
如上图所述,客户端最开始使用 4G 移动网络访问业务,源 IP 假设为 IP1,整个访问流程使用蓝色线条标识。
当用户进入 WIFI 网络时,源 IP 发生了变化,从 IP1 切换到了 IP2,整个访问流程使用绿色线条标识。由于接入的 CLB4 有可能发生变化,但整个 CLB 集群统一使用 QUIC Connection ID 调度,只要 QUIC 连接的 ID 没有发生变化,能够将该请求调度到相同的 CLB7 层机器上。
同一台 CLB7 保存了相同的 Stream 及 Connection 处理上下文,能够将该请求继续调度到相同的业务 RS 机器。
整个网络和 IP 切换过程,对于用户和业务来讲,没有任何感知。
动态的流量控制和拥塞控制
STGW 在连接和 Stream 级别设置了不同的窗口数。
最重要的是,我们可以在内存不足或者上游处理性能出现问题时,通过流量控制来限制传输速率,保障服务可用性。
性能统计
STGW 针对 QUIC 的线上使用情况进行了很多的变量统计和分析,包括 0RTT 握手成功率,握手时间,密码套件使用分布,QUIC 协议版本,stream 并发数量等。
这些统计变量能够为我们的协议优化提供更加精细的数据支撑。
QUIC 线上灰度数据
QUIC 目前已经在 STGW 上线运行。我们针对腾讯几个重要域名(包括 QQ 黄钻页面,游戏页面)进行了灰度实验。
Qzone QUIC 页面
如上图所示,图中红色箭头指向的绿色标识表示该页面使用了 QUIC 协议。
灰度实验的效果也非常明显,其中 quic 请求的首字节时间 (rspStart) 比 http2 平均减少 326ms, 性能提升约 25%; 这主要得益于 quic 的 0RTT 和 1RTT 握手时间,能够更早的发出请求。
此外 quic 请求发出的时间 (reqStart) 比 h2 平均减少 250ms; 另外 quic 请求页面加载完成的时间 (loadEnd) 平均减少 2s,由于整体页面比较复杂, 很多其它的资源加载阻塞,导致整体加载完成的时间比较长约 9s,性能提升比例约 22%。
上述数据有两个问题,仅供参考:
由于我们的页面并没有全部改造成 QUIC 协议,所以性能数据应该还可以进一步提升。
每个业务的页面构成不一样,提升的性能数据也会有差别。
CLB-QUIC-DEMO
前面提到的 QUIC 实践和优化都是针对服务端。为了方便广大开发者进一步了解 QUIC 在客户端的使用,我们提供了一个安卓客户端的 DEMO,仅供参考。
DEMO 已经在 github 上开源,地址如下:
https://github.com/tencentyun/clb-quic-demo
DEMO 的主要目的有两个:
简单说明一下在客户端使用 QUIC。
简单对比 HTTP2 和 QUIC 的性能差别。
如果有用户想使用 QUIC 协议,客户端做一下改造,服务端直接使用腾讯云 CLB 负载均衡器就能实现了。
如前所述,CLB 在协议计算性能和访问速度、安全性能方面,做了非常多优化。
结 论
QUIC 协议非常复杂,因为它做了太多事情:
为了实现传输的可靠性,它基本上实现并且改进了整个 TCP 协议的功能,包括序列号,重传,拥塞控制,流量控制等。
为了实现传输的安全性,它又彻底重构了 TLS 协议,包括证书压缩,握手消息,0RTT 等。虽然后续可能会采用 TLS1.3 协议,但是事实上是 QUIC 推动了 TLS1.3 的发展。
为了实现传输的并发性,它又实现了 HTTP2 的大部分特性,包括多路复用,流量控制等。
虽然如此复杂,但是 QUIC 作为一个新兴的协议,已经展现了非常强大的生命力和广阔的前景。
目前国内外除了 Google 大规模采用外,还鲜有其他互联网公司使用。STGW 作为腾讯的安全云网关,我们有责任,有义务对业界先进的标准协议提供支持和优化。同时腾讯云也是国内第一家支持 QUIC 协议的云厂商,因为这个协议能切实改善客户端的访问速度和终端用户体验。
我们不仅在服务端实现了 Quic 协议的支持,优化了 QUIC 协议方面的性能问题,同时也希望通过自己一些经验的分享,推动 QUIC 协议的发展,构造一个更加安全更加快速的互联网世界。
Let’Quic, Make Web Faster。