通过敏捷快速实践 QUIC ,网易新闻在3个月内,将端内 QUIC 请求占比提升到 75%+,如 图1 所示,并将客户端请求平均响应时间 RT 降低了 45%,请求错误率降低了 50%+,视频卡顿率降低了 25%+。
图1 客户端 QUIC 请求占比
(黄色部分为 QUIC 请求,蓝色部分为其他协议请求)
如何做到以最小的成本、最小的风险和最大的收益接入 QUIC,是本文讨论的核心。
什么是QUIC?
QUIC 是 Quick UDP Internet Connections 的缩写,意为“快速 UDP 网络连接”。QUIC 由 google 实现于2013年,是一种网络传输协议,旨在提升网络传输速度。2015年,QUIC 被提交到 IETF,目标是成为下一代的正式网络规范,2018年,HTTP over QUIC 被 IETF 重命名为 HTTP/3。
在 UDP 之上,QUIC 实现了类似 TCP 的丢失重传机制,QUIC 传输以数据包级报头发送,并对每个包增加了单调递增的数据包号来代表传输顺序,当检测到必要帧丢失时,QUIC 会将必要帧绑定到新数据包重发。QUIC 对报文头部和数据也都进行了加密,且建联时改进使用了 DH 密钥交换算法,在防劫持方面也具有一定优势。
所以,QUIC 虽然基于 UDP 实现,但在功能上等价于 TCP + TLS + HTTP/2,除此之外,相较于传统的 HTTP + TCP,QUIC 还具有多项改进网络传输的优势,其部分优势如 图2 所示。
图2 QUIC表现优势
QUIC 目前分为 gQUIC 与 iQUIC 两种,gQUIC 即为最初的 Google QUIC,而 iQUIC 是后来 IETF 制定的通用传输协议,如 图3 所示。
图3 QUIC分类
相较 iQUIC 而言,gQUIC 目前的应用较为普遍、成熟,如 Cadddy 支持 gQUIC,客户端还有 Chromium 的 Net 库 Cronet 也可以支持 gQUIC,包括 ExoPlayer 等三方库也都提供了对于 gQUIC 的扩展支持。目前来看,选择 gQUIC 对于渴望改善网络传输情况的开发者来说,在接入成本和接入效率上具有优势。
QUIC 接入策略
为了快速接入并验证 QUIC,在流量入口设计上,我们选择用 Nginx + Caddy 方案实现;在客户端网络库上,我们选择了 Chromium 的网络库 Cronet,原因如下:
- Google 对 Chromium 开源多年,已经过众多软件团队的验证,基础功能稳定良好;
- 作为 Chromium 的网络库,Cronet 的跨平台性良好,在 Android 和 IOS 双端可可直接接入;
- gQUIC 相比 iQUIC,在各方面支持都更成熟,Cronet 也可以直接支持 gQUIC;
2.1 流量入口
流量入口设计上,由 Nginx 负责处理 HTTP 请求,Caddy 负责处理 UDP 请求,如 图4 所示。
图4 流量入口设计
2.2 网络库方案设计
在客户端网络库处理上,如何能保证侵入性最小,风险性最低,并最快接入 QUIC,以验证线上收益,成为了项目的主要目标。
由于客户端用户规模较大,一些量级较小的第三方 QUIC 处理库使用存在一定风险性问题,而自研 QUIC 库支持的成本和效率远远达不到预期,且 QUIC 的网络改善收益对于 Team 来说是未知的,选择一款稳定可靠的网络库来快速达到 MVP(最小可行性) 是首要目标,所以在 Android 和 iOS 端,我们都选择了使用 Cronet。
2.2.1 Android
Android 端目前使用的网络库是 OKHttp,为了更清晰有效地验证 QUIC 的效果,我们在接入 Cronet 的同时,也保持了原有网络库 OKHttp 处理逻辑的不变,通过动态配置控制 AB 策略。上线后通过逐渐切量,一部分网络请求走 Cronet,一部分网络请求走 OKHttp,以清晰对比出二者的差异。
而端内目前的两套数据上报机制,一种基于 OKHttp 事件监听实现,一种通过 Hook OKHttp 的事件监听实现,而 Cronet 的上报与 OKHttp 事件回调可以对齐统一,可维护性良好,再次增加了我们选择 Cronet 的理由。
2.2.2 iOS
Cronet 在 iOS 接入方面,API 设计友好,基于 NSURLProtocol 通过网络拦截即可实现。iOS 的接入流程可以简单分为初始化 Cronet 和注册 NSURLProtocol 协议两个部分。
2.3 网络库接入
2.3.1 Android
在接入 QUIC 时,Android 使用网络库 OKHttp 的拦截器(Intercept)机制来处理网络请求较为便捷,当网络请求从业务顶层通过 OKHttp 传输到 Cronet 对应的拦截器时,会交由 Cronet 处理。
2.3.1.1 拦截器处理
OKHttp 拦截器(Intercept)设计基于责任链模式,由于在拦截器中接入 Cronet 后,网络请求会被 Cronet 库拦截处理,造成后续的拦截器短路,中断 Request 与 Response 信息在拦截器中的传递,所以 Cronet 的拦截处理就应尽量下沉,置于顶层应用拦截器的最底部,如 图5 所示。
图5 QUIC拦截器处理
而对于下层的默认拦截器和自定义网络拦截器都将被 QUIC 短路,默认拦截器中主要提供了缓存、Gzip 解压、建联等处理,其中一部分处理策略由 Cronet 也可以替代实现,而重定向一类策略在 Cronet 监听回调中处理也较为便利。
使用 Cronet 发起请求提供两种实现,一种是 CronetUrlRequest,另一种是符合 HttpURLConnection 标准的,在 CronetUrlRequest 基础上封装了流操作的 CronetHttpURLConnection,由于客户端涉及多媒体场景较多,使用 CronetHttpURLConnection 更能满足大部分需求场景。
目前,我们在视频、图集、列表通用业务网络请求等范围全面实现了 QUIC 的接入。
2.3.1.2 数据衡量
为了更明显地对比出 QUIC 与 HTTP/HTTPS 之间的数据变化,我们使用了两套网络数据监测机制:全链路统计和 APM ,以精准捕获特定请求事件的时间戳,来计算并上报建联时间、响应时间等信息。
通过 Cronet 提供的一些回调,可以实现与 OKHttp 上报监听的逻辑统一。Cronet 提供的 RequestFinishedInfo 可以获得 Request 的建联等信息,提供的 UrlRequest.Callback 可以获得响应等信息,而使用 RequestFinishedInfo 会增加一部分监控逻辑,对 Cronet 有轻微的性能折损,仅使用 UrlRequest.Callback 返回的部分响应信息也足够得出 QUIC 性能数据,全链路上报 OKHttp 与 Cronet 监听对齐方案如 图6 所示。
图6 全链路上报事件
由于 Android Cronet 的 CronetHttpURLConnection 在设计上较为封闭,扩展性并不是十分良好,比如响应回调 UrlRequest.Callback,或是内部实现的阻塞队列控制 MessageLooper 都没有暴露给开发者,在此基础上定制一些功能实现,如全链路上报,或是 CronetHttpURLConnection 没有提供的超时时间策略,都不是很方便。
为了解决这个问题,我们建立了一层代理和装饰器,通过拦截 CronetEngine 可以拿到 CronetHttpURLConnection 的 UrlRequest.Callback,对此再做一层包装,由包装类进行统一的事件分发,将 Response 事件分发到了 OKHttp 的全链路处理器中,进而实现了全链路上报设计的统一。
2.3.1.3 遇到的问题
(1)超时时间处理
由于 CronetHttpURLConnection 提供的部分超时时间设置是空实现,而通过 Native Cronet 设置的超时时间也并不符合预期,通过 ConditionVariable 实现超时也未免过于冗余。
CronetHttpURLConnection 内部维护了一个类似于安卓处理管理机制 Message + Looper 的 Task 处理器——MessageLooper,MessageLooper 中维护了一个阻塞队列,并提供了超时的控制策略,通过建立同包名类间接继承 package 访问权限的 MessageLooper,通过反射替换封闭的 MessageLooper 为 NRMessageLooper,进而可以获得超时时间的灵活控制权,在此基础上,我们增加了超时时间的动态下发,以便于风控配置。
对 Cronet 库的定制如 图7 所示。
图7 网络库定制架构图
(2)请求占比优化
最初,客户端 QUIC 协议请求占比在统计上报上来的数据观测中并不理想,为了提升 QUIC 占比,我们在与 CDN 共同排查后,优化了双方的处理方案,客户端转用 CronetHttpURLConnection 处理,CDN 侧同时优化处理,并对网络节点也进行了相应调整,保证了两侧方案的统一性,并再次降低了平均响应时间与错误率。
2.3.2 iOS
在 iOS 网络库接入方面,调用 Cronet 的 setMetricsEnabled 接口开启 Metric,通过 URLSession 回调获取 networkProtocolName 属性,可以获取当前网络请求的协议类型,以更有针对性地进行数据衡量。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0))
QUIC 项目上线
3.1 切量控制
由于 QUIC 改善的拥塞控制与更低的建联时间,都传递给我们了一个信息:QUIC 对改善视频卡顿等场景有所增益,所以,我们最初的方案选取测试视频域名,而最终视频的卡顿率也有所优化,在弱网场景则更加明显。
由于切量到 QUIC 测试,视频域名需要同步从 HTTP 切换到 HTTPS,这样会增加 QUIC 测试干扰项,为了更好地观察数据,我们设计了 QUIC 的两套切量策略,保持一部分原有域名的请求,并将又一部分原有域名也切换到 HTTPS,而 QUIC 测试将另一部分原有域名切换为 CDN 域名进行测试,切量控制如 图8 所示。
图8 QUIC动态配置
而由于 QUIC 存在较高的未知性,切换网络库对于大量级的网易新闻来说,存在的风险较高,而产生异常对用户影响也极大,所以 Team 采取了 CDN 先预热,后动态下发配置,筛选目标用户进行逐步放量的模式,历经三个版本,完成 QUIC 的测试。
3.2 项目里程
QUIC 立项前开始零零散散的一段调研期,而后4月中旬接入,各方1周完成敏捷开发,以 MVP 最小可行性原则快速完成功能开发,后续联调、上线测试和逐步迭代改进,持续共计 3 个月左右,以团队预期的速度稳定探索,最终取得了较为理想的测试效果。
数据表现
4.1 平均响应时间与错误率
通过全链路上报的信息,可以很直观的导出 QUIC 与原有请求传输方式的对比信息,通过放量控制,同一 Host 下,我们使 QUIC 协议请求量与 H2 对齐,其 Count 曲线如 图9 所示。
(以下图中,蓝色皆为H2曲线,黄色皆为QUIC曲线)
图9 请求量对比
得出的 QUIC 与 H2 平均响应时间对比如 图10 所示,可以直观的看到,QUIC 平均响应时间较 H2 缩减了约 45%。
图10 平均响应时间对比
而 QUIC 带来的错误率也大幅降低,在错误率表现上同样优秀,如 图11 所示。
图11 错误率对比
4.2 弱网表现
基于业界的部分研究结论,我们对 QUIC 在弱网的表现也进行了监控。
这里的网络情况以获取到的 手机网络信号强度 LTE 等级 为依据,如下数据观测基于网络信号强度划分的 6 个等级,其中:
- 0、1、2 这三级代表弱网情况;
- 4、5 这两级代表一般网络情况。
4.2.1 平均响应时间
同样保持请求量对齐,在该数据观测定义的弱网场景下,QUIC 与 H2 的平均响应时间对比曲线如 图12 所示。
(以下图中,蓝色皆为H2曲线,黄色皆为QUIC曲线)
图12 弱网场景下RT
在该数据观测定义的一般网络场景下,QUIC 与 H2 的平均响应时间对比曲线如 图13 所示。
图13 一般网络场景下RT
从弱网场景统计曲线的更大差值,可以粗略得出结论,QUIC 在弱网场景下,请求响应时间优化更明显。
4.2.2 错误率
在该数据观测定义的弱网场景下,QUIC 与 H2 的错误率对比曲线如 图14 所示。
图14 弱网场景下错误率
在该数据观测定义的一般网络场景下,QUIC 与 H2 的错误率对比曲线如 图15 所示。
图15 一般网络场景下错误率
从弱网场景统计曲线的更大差值,同样可以粗略得出结论,QUIC 在弱网场景下,错误率优化更明显。
4.3 视频性能
在视频性能方面,我们分别针对卡顿率和1秒率进行了监控。
4.3.1 卡顿率
从 图16 可见,QUIC 在视频性能表现上,相比 H2 请求,卡顿率下降约为25%+。
(以下图中,蓝色皆为H2曲线,黄色皆为QUIC曲线)
图16 视频卡顿率
4.3.2 1秒率
这里的 1秒率 是指视频在1秒内达到播放状态的比例,从 图17 可见,QUIC 在1秒率表现上,相比 H2 请求有一定幅度提升。
图17 视频1秒率
后续展望
对于在 Cronet 的定制和处理方面,还有更多的改进空间,如协商过程的优化,DNS 的定制优化处理等,后续我们会逐步精进,继续深入挖掘 QUIC,期待发现更多的惊喜。
作者简介
李云鹏 2016年加入网易传媒,目前主要负责架构和性能优化相关工作。QCon 大会讲师,著有《移动开发架构设计实战》等书籍。
李鑫飞 网易新闻客户端 iOS 架构组组长,负责客户端基础架构,组件化及性能优化等工作。
张 鑫 网易资深应用运维工程师,负责网易新闻内容发布系统运维工作,在内容发布、流媒体点播、直播、CDN运维、全链路监控、大数据运维、业务容器化方面有丰富运维经验。
鸣谢
发起人与项目组其他成员;
项目发起人:技术总监 棉明;
Android:技术专家 广丛;
iOS:资深工程师 庞博;
运维及杭研团队等。