近日,快手自研高性能服务器 kQUIC 已全面上线,集群峰值 QPS 突破千万。kQUIC 是快手结合自身业务特点,自研打造的支持 QUIC/HTTP/HTTPS 多协议同层接入的高性能服务器,在短视频场景下针对行业痛点做了系列优化。InfoQ 记者就此专访了 kQUIC 团队多位核心开发者,挖掘了其自研背后的架构设计演进、技术难点攻关以及未来发展规划等问题,以飨读者。
项目背景
QUIC 协议,全称 Quick UDP Internet Connections,是由 Google 开发的基于 UDP 的新一代互联网传输协议。顾名思义,其代表特点就是“快”,相比 HTTP(s) 等协议,它具备以下几大优势:
首先,QUIC 在应用层实现了基于 UDP 的可靠传输,能够在使用多路复用机制的同时避免 TCP 的队首阻塞问题;
其次,QUIC 改进了握手机制,大部分场景下可以 0-RTT 建立安全链接进行数据加密发送;
再次,QUIC 同时设计了连接迁移的新特性,可以保证在用户的网络地址发生变化时,比如 WIFI 和 4G 切换,业务请求依然能够被继续处理,不需要重新发起请求;
最后,QUIC 传输控制机制在应用层进行处理,相比 TCP 在内核中处理的方式更灵活、方便贴近业务场景做自定义优化,可以应用更先进的算法进行双端加速。
在快手内部,早期曾尝试过业界主流的两种 QUIC 实现方案:gQUIC(Google QUIC)和 iQUIC(IETF QUIC)。二者均作为独立模块使用,架构上采用了多层架构方案,QUIC 和 NGINX 作为接入层的两个环节独立部署。
对用户请求如何由 QUIC 层转发到 NGINX 层,快手也尝试过统一部署与通过内网 LB 转发两种策略,但无论是哪种方案都存在一定短板,无法满足快手内部对“快与稳”的极致追求。多层架构使得接入链路变长,对性能和稳定性都有影响,也大大增加了服务器和日常维护的成本。
在这样的背景下,经过充分的技术调研和深入的对比测试,快手最终决定自研 kQUIC 解决方案,通过在 NGINX 中集成 QUIC 支持并且对 QUIC 协议栈进行大量优化改造,不断挑战更优质的用户体验。
kQUIC 演进历程
kQUIC 自 2019 年上半年开始研发,年底灰度上线,2020 年 6 月峰值 QPS 突破千万,历时一年有余。kQUIC 针对性地对数据压缩、网络传输拥塞控制等进行了优化,传输耗时平均降低 10% 以上,服务端最大 QPS 提升了 50%。
与此同时,配合客户端网络库的统一和性能优化,实现 QUIC 协议接入,通过 A/B 实验证明连接复用率正向上涨 24.85%,总耗时均值正向下降 42.46%。对于弱网用户的网络体验通过实验长期观测也明确了置信的收益,实验组用户数有稳定的提升。
kQUIC 团队的核心开发者向 InfoQ 记者透露了其从选型、开发到部署上线的演进全历程。
技术选型:开源还是自研?
快手技术团队在决定自研 kQUIC 前,曾调研过国内外的 QUIC 开源实现方案,主要有以下几种:
Google:Google 开源了跨平台的 Chromium,其中包含了 QUIC 协议栈,还附带一个简单的 Server Demo,只能用作功能验证,性能、可运营性都无法满足生产环境需要。此外 Google 的原生实现里还阉割了一些实用特性,比如对 ecc 证书的支持等。
Microsoft:MsQUIC 今年才正式开源,kQUIC 立项之初 MsQUIC 尚未对外发布,且主要针对 Windows 跨平台,目前功能上并不完善,也不支持 0RTT。
NGINX:NGINX 官方对 QUIC 的支持还处于 Preview 阶段,功能上存在短板,尚不能应用在生产环境下。
基于快手已经在使用 NGINX 作为 7 层负载均衡服务的考虑,为了复用当前的运营手段和基础设施,kQUIC 团队选择了在 NGINX 上自研的道路。
接入层服务架构设计与部署方案
为了能够充分利用 QUIC 的优势提升用户体验,快手决定把 NGINX 和 QUIC 结合起来。通过一系列的性能和功能优化手段,实现了在 NGINX 上同时高效支持 HTTP/HTTPS/HTTP2/QUIC 等协议的目的,同时可以根据用户不同的网络条件综合各协议的能力,达到最优的访问质量。
上图为服务端方案整体架构,从 NGINX 内核集成 QUIC 协议栈,实现多协议同层接入,到 Linux 内核优化 UDP 报文管理,实现性能提升。
早期快手的 QUIC 接入层选择采用多层架构,QUIC 层与 HTTP 代理层独立部署,用户请求经过四层 LB 转发后,通过 QUIC 协议层处理,再通过 HTTP 代理层处理传递到服务端。而在 QUIC 协议层与 HTTP 代理层之间,快手也尝试过同机部署与通过内部 LB 转发两种架构。但不论是哪种架构,都会因为多层部署以及层级间的依赖,带来运维成本、资源成本以及稳定性成本的增加。因此,这些架构并不完全适用于生产环境部署。
上图为同机部署架构
上图为内网 LB 转发示意图
为了解决多层架构部署的弊端,快手团队打造了一个适配全站 QUIC 接入的全新方案。新方案部署架构最基本的要求,就是简单与稳定,期望只在一层上就实现 QUIC/HTTP(s) 协议的接入处理。即在现有的接入层代码中,嵌入 QUIC 逻辑,实现 QUIC/HTTP(s) 协议同层接入。
上图为 kQUIC 部署架构
具体而言,和绝大多数公司一样,接入层仍旧采用多集群部署,集群规模由压测出的安全阈值来决定,预留一定的 buffer,放量过程中依据监控情况可以随时进行调整。实际上,用户是否通过 QUIC 协议请求是由服务端与客户端共同协商的,这里 kQUIC 采用的是业界主流方式,由服务端返回 QUIC 相关的 header 告知客户端可以通过 QUIC 协议来进行请求,在接入层配置上无需做太大改动,只要针对相关域名开启 QUIC 支持即可。
接入链路上,kQUIC 也与传统的四 / 七层接入并无太大差异,流量经过四层网关转发到七层 (kQUIC) 上,再由七层 (kQUIC) 转发给后端的 server。为了适配 QUIC 协议,kQUIC 在四 / 七层网关上都做了相应的支持,确保 QUIC 请求能够按照正确的规则转发。
在部署方案上,快手内部有一套成熟的配置发布变更平台体系,接入层的配置变更也早已平台化,依托于平台可以很方便地实现新协议的接入。
自研网络拥塞控制算法
网络拥塞是指网络处于一种持续过载的状态,即用户对网络资源的需求超过了固有的处理能力和容量,一般会出现数据丢失、时延增加、吞吐量下降的现象,严重时甚至会导致“拥塞崩溃”。拥塞控制是避免和解决网络拥塞的重要手段之一,其核心在于依据当前网络状态,动态决定“在什么时候,以多大的速率,发送多少数据”。
快手自研了一套基于 inflight 的拥塞控制算法 IA2C (Inflight Aware Congestion Control),其核心思想在于动态控制网络中的 inflight 数据包的数量,在传输延迟、吞吐量之间取得 tradeoff,从而在避免网络拥塞的前提下,保证传输的时效性与高效性。与传统的拥塞算法相比,IA2C 采用更有效的统计指标、动态粒度的统计信息、更高效的控制逻辑等,从而避免了网络噪音的干扰,能适应更多更复杂的网络环境。
在网络良好的情况下,与 BBR、Cubic 等算法性能相当,都能达到高带宽、低延迟效果,总体差异不大。但在弱网环境下,IA2C 替换默认算法所取得的提升效果十分明显。
0RTT 成功率与加密性能优化
0RTT 是 QUIC 的一个重要特性。一般而言,客户端会缓存服务端下发的 SCFG,并且在后续握手的时候把 SCFG 对应的 ID 发给服务端,服务端根据 ID 查到历史上下发的 SCFG,这样客户端和服务端就可以在共同的 SCFG 基础上来完成握手。但是服务端的 SCFG 是随机生成的,服务器 A 生成的 SCFG 对应的 ID,在服务器 B 上是不能识别的,这样就限制了 0RTT 的成功率。
为了解决这个问题,kQUIC 做了两个解决方案:
如果对安全性要求不高,可以让每个服务器都生成相同的 SCFG,这样就实现了跨服务器的 SCFG 识别,可以有效提高 0RTT 的成功率。
对安全性要求高的场景,则搭建一个 SCFG 集群,每个服务器生成的 SCFG 都在集群中向所有服务器共享,这样某个服务器收到自己不能识别的 SCFG 的 ID 后,可以从集群中找到对应的信息,也可以达到提高 0RTT 成功率的目的。
加密性能又分成两个方面,一个是非对称加密,另一个是对称加密。
针对主要消耗 CPU 的非对称加密,kQUIC 做了两个优化:
使用加速卡集群来 offload 握手时的非对称加密的计算开销;
优化了 Chromium 中的 QUIC 协议栈握手部分的实现,去掉了不必要的二次签名操作,降低了计算代价。
针对对称加密的部分,优选 AES 加密算法,利用 Intel 的 CPU 指令集来加速计算。在 CDN 等“不需要”加密的场景,自定义一个明文的“加密”算法,直接去掉了对称加密,有效减少了 CPU 的开销。
客户端、网络库统一设计
对 QUIC 协议的支持需要客户端、服务端统一设计,kQUIC 也做了相应的工作。
客户端网络库项目代号是库 Aegon,目标是代替原 OKHTTP/AFNetworking 和进行 API 请求和短视频下载,提供了 QUIC 协议的支持、完善的上报信息,并基于对数据指标的分析和对协议的深入理解,对网络库中持续进行了多项协议相关的优化,包括预建连、SSL Session 复用优化、客户端 BBR、POST 请求 0RTT 优化等等。
一般 APP 使用的开源的网络库包括 OKHTTP 和 AFNetwork,都不支持跨平台,OKHTTP 是 Android 端,AFNetwork 是 iOS 端。快手网络库在设计之初就把跨平台作为一个重要的目标,为快手的双端提供统一的网络优化解决方案。
难点攻关
很多时候,自研并不是一条康庄大道,QUIC 协议发轫于 2013 年,经过 7 年时间却也远未成熟,业界并没有太多开源实现方案可供参考。快手短视频、直播的业务特点也带来了一些新问题需要解决。
一方面,快手接入层的体量大,QPS 超过千万,作为用户使用快手服务的入口,对稳定性和性能有极高的要求。而 QUIC 协议栈的实现非常复杂,在和 NGINX 结合的过程中,很容易遇到问题,给稳定性带来极大挑战。
另一方面,QUIC 的性能仍不够理想,如何优化提升整机的性能是一大关键。
第三,NGINX 的配置热更新机制,做不到 UDP 业务无损更新,运营层面难以接受,如何解决也是一大难题。
kQUIC 结合业务场景,从压缩、对称 / 非对称加密、报文发送等方面进行了优化,取得了不错的性能提升效果。此外,通过在 Linux 内核中构造 QUIC 的 session 表,以此保证 NGINX 在 reload 之后能够做到旧连接的报文继续发往旧进程,新连接的报文可以发往新进程,实现无损配置更新,满足了运营侧的需求。
kQUIC 的现状与未来
在实现了全站全量上线、千万级 QPS 集群、传输耗时平均降低 10% 以上、服务端最大 QPS 提升 50% 的优异成果后,kQUIC 团队也谦虚地表示 kQUIC 目前还有一些不足,功能上也正在迭代,如秒级的细粒度统计监控、适合超大规模后端集群的故障屏蔽与恢复机制等等。
kQUIC 未来会持续迭代功能和性能,同时跟进 HTTP/3 的进展,适时向 HTTP/3 靠拢。此外还将探索 MPQUIC 等机制,尝试在一些业务场景上落地,获得收益。
目前行业内已经有了一些针对 QUIC 的开源项目,这些项目给技术团队带来了很多启发和思考,对于 QUIC 的推广和应用起到了积极的作用。但每个行业、企业对于 QUIC 都有个性化的需求,开源方案现阶段很难直接解决某些具体的问题。
kQUIC 团队表示,kQUIC 目前主要是基于快手现有业务做的技术适配,还有优化和升级的空间,对外开源还不够成熟。kQUIC 当前对行业的价值主要在提供一种可行的技术思路,在哪些方面做优化和改善可能为 QUIC 落地到具体业务中起到积极作用,哪些快手尝试之后感觉收益不明显,这些实践经验在现阶段相比开源可能更具备价值,后续 kQUIC 团队也将安排专门的技术交流活动,希望与业界一起交流。
写在最后
受国际环境影响,开源与自研两条腿走路的方案已经被广大中国互联网企业所重视,做好技术创新、技术储备已经是国内科技企业的共识。快手成立 9 年以来,一直在耕耘技术,在各种不同的业务场景下,想要为用户提供上佳体验,让用户保持新鲜感,就需要企业不断实现技术创新,这是快手内部的一种共识。
相信随着快手体量的持续扩大,未来还将会有更多来自快手内部的技术实践落地经验成为可供业界参考的解决方案,不论是开源方案还是实现思路都是对中国技术力量的一种有效反馈。InfoQ 也将为业界带来更多中国科技企业的技术实践,一起为中国技术力量的建设添砖加瓦。
受访嘉宾介绍
李隆,快手系统运营部 SRE,负责快手 API 7 层接入服务的架构设计与运维部署方案优化与实施工作。
沈坤,快手系统运营部 CDN 架构师,负责快手 CDN 的架构设计和开发工作,同时做一些新技术、新协议在 cdn 和 7 层接入的落地与优化工作。
孙炜,快手平台研发部,负责长连接接入、协议优化及 SSL offload 的架构与研发。
郭君健,快手音视频体验中心负责人,负责客户端网络库接入方案,以及测试和实验方案设计,通过数据分析支持项目上线和迭代优化。
评论