QUIC 致力于成为下一代的互联网传输协议,很自然地,要能解决当前已有的 TCP 协议存在的若干不足之处。
QUIC 的优势
我们先来了解一下 QUIC 协议的几个主要优势:
- TCP 协议在建立连接时,需要经历较为漫长的三次握手行为,而在关闭时,也有稍显冗余的 4 次摆手。而 HTTPS 初始连接需要至少 2 个 RTT 交互(添加了握手缓存就会变成了 1-RTT,这里指的是 TLS 1.2),外加 TCP 自身握手流程,最少需要 3 次 RTT 往返,才能够完整建立连接。而 QUIC 协议层面界定了 1-2 个 RTT 握手流程,再次连接为 0-RTT 握手优化流程(但需要添加握手缓存)。
- 一旦网络异常,会导致 TCP 出现线头阻塞(Head-of-line blocking),后续数据将被阻塞住;同时针对数据包重传的会导致 RTT 延时测不太准。人们在使用 HTTPS 或 HTTP/2 时,可能因为 TLS 协议层面一个记录(record)丢失,导致后续其它的记录一样会被阻塞住,出现双重阻塞,这基本上是无解的。TCP 丢包阻塞机制导致无法实现真正的多路复用;而 QUIC 协议层面界定了流与流之间独立的特性,就算是丢包,流与流之间也不会互相影响,实现了真正意义上的多路复用机制。
- TCP 协议栈被嵌入内核层,导致协议升级迭代操作十分不便,尤其是遗留的中间网络设备和正在服务中的机器;而 QUIC 可以直接部署在用户层,和上层应用程序绑定在一起,随着上层应用程序更新而升级。
这样看来,QUIC 是一个为解决 TCP 存在的缺陷,为未来设计的全新传输层协议。
QUIC 在微博的应用
除了 HTTP/HTTPS 常规连接通道之外,我们想让微博 APP 在初始连接时在网络链路层面请求后端业务时能够再快一点,在弱网环境下尽可能满足用户基本请求。微博 APP 要能够在复杂多变的网络环境下有更多的链路通道选择,增加后端业务的可用性。基于这些考虑,我们将目光对准了 Google QUIC 协议,开始在线上进行尝试性部署、运维支持。
目前大家普遍是在支持 QUIC 的浏览器端(比如 Google Chrome)进行尝试性实践,比如腾讯 QQ 空间等。
微博目前将 QUIC 应用在了移动端业务中,在 Android 和 iOS 版本的微博 APP 中,内置了对 QUIC 的支持。同时通过灰度策略,选择部分用户将常规 HTTP API 请求通过 QUIC 传输。目前大家手机上所安装的微博 APP,只要不是版本太陈旧,都会有 QUIC 依赖库的支持。
除此之外,微博也在尝试在更多业务场景下推广使用 QUIC 协议时积累的优化、线上实践和运维经验,实现整个集团资源共享:
- 移动终端 App 环境下,手机端针对 Cronet 做定制和封装,简化兄弟客户端团队 App 调用成本。
- 完善的一行命令搞定简化的 QUIC Server 部署解决方案等,包括物理机以及 Docker 虚拟化等。
- 兼容来自于 Web 端和移动 Web 浏览器端的 QUIC 业务请求,方便兄弟业务团队快速接入。
微博选择开源的 Caddy 运行 QUIC Server(下面简称为 QUIC Server),在物理机部署环境下采用的是 LVS DR 网络拓扑模式,在实际运维操作过程中遇到以下若干问题。
第一个问题,在部署层面,LVS DR 模式下针对 UDP 支持没有 TCP 完善,Real Server(后面简称 RS)上程序绑定 UDP 端口监听,只能显示的绑定公网 VIP,比如:bind 114.114.114.114:443 。而 LVS 需要定时对 RS 进行心跳检测,而心跳走的是 RS 内网 IP,我们直接使用 netcat 搞定 RS 内网 IP 和端口的绑定:
ncat -u -l 10.10.10.10 443 -k -c 'echo "hello"'
这样才算完整。
第二个问题,现实情况是,QUIC Server 要和 Nginx 混合部署,而 Nginx 已经占用 HTTPS 443 端口。为此我们进行了监听端口逻辑的微调:
-
Nginx 继续监听 HTTPS 443 端口,但在所有输出响应的 HTTTP 头部,新增一行内容: Alt-Svc: quic=":443"; ma=2592000; v=“38,37,36” ,用于告诉客户端后续请求请切换到 QUIC 通道上
-
修改 QUIC Server 不再监听 TCP 443 端口,而只监听 UDP 443 端口,只负责 QUIC 请求
这样实现两者协同共存,上帝的归上帝,凯撒的归凯撒,各司其职。
其实若使用 Docker,不对外暴露 TCP 443 端口就可以避免上述问题。
第三个问题,微博 APP 支持 QUIC 协议的初始版本,访问 QUIC Server 一直都是 2-RTT 才能完成握手建连,握手交互频率过于频繁,因此服务端针对整个握手机制做了优化,调整为现在初始 QUIC 连接都走 1-RTT 握手流程,节省了一次链路层往返开销。
第四个问题,QUIC 客户端存在网络制式切换,就算是同一个移动机房,可能第一次业务请求时会落到 A 这台 QUIC Server 实例,后续再次连接,就会落到 B 实例上,重复走 1-RTT 的完整握手流程。为此微博实现了全局级别的握手缓存机制,用户网络发生切换时,下一次的业务请求无论是落到哪一个机房或哪一台实例上,握手建连都会是 0-RTT。
第五个问题,在一些 NAT 网络环境下,UDP 协议会被路由器等中间网络设备禁止,这时客户端 SDK 会直接降级,选择 HTTPS 等备选通道,保证正常业务请求。
我们所遇到问题和挑战,以及困难的解决,跟张旗、史大龙和胡长利等团队成员的共同努力是分不开的,在此也对大家的工作表示感谢。
下面再来看看使用 QUIC 之后的效果。
健康网络情况下,二者区分并不明显。但在弱网环境下,QUIC 要优于 TCP。
1. 在丢包率较高的场景下,QUIC 的接口请求成功率明显优于 HTTPS,在丢包率较低的场景下无明显优势。
2. 在请求耗时方面,丢包率的上升对 QUIC 的影响要远小于 HTTPS。
3. 在文件传输在有网络延迟的情况下,QUIC 的耗时表现也优于 HTTPS。
4. 针对某一接口的线上数据对比,采用 QUIC 协议的接口请求成功率,比 Original 下的 HTTPS 有 1.01% 的提升。
总之,QUIC 在弱网环境下相比 TCP 有所提升,针对 CDN、流式传输等一些特殊场景也会有一些提升效果;总体上效果不是那么明显,甚至在一些场景下表现还不如 TCP。
近期来看,QUIC 完全取代 TCP 不太可能,但作为一种通道的存在,让我们多了一种链路传输选择。
QUIC 的未来发展
QUIC 致力于成为下一代的互联网传输协议,因为其定位于操作系统用户态,这就意味着在移动终端环境下,具体 APP 可以包含该协议某一个较为完整的演进版,第三方依赖库形式可插拔、可配置,毫无疑问 QUIC 协议的更迭相比 TCP 的修修补补,灵活得多。
目前 QUIC 协议在 IETF 工作组中还处于细节的频繁修改中,IETF 工作组也给出了一些协议实现建议,第三方实现也是百家争鸣。但随之而来的是,人们会面对选择困惑,若要应用于实际,就需要花费很多精力去抉择,是使用 Google QUIC 的实现还是 IETF QUIC 第三方实现,但能够达到线上可运行级别的实现却是凤毛麟角。
随着 TLS 1.3 草案有可能 2018 年定稿为 RFC 技术规范,我们有理由去期待 QUIC 在 2019 年内形成 RFC 技术规范。虽然这并不妨碍我们使用支持某一个 drafts 草案版本 QUIC 实现,但要面对下一个草案是否需要选择支持的困惑。
总之,QUIC 是一款非常伟大的协议,随着自身的逐渐成熟,注定要和 TCP 在相当长的一段时间内并存,相互依赖。
关于作者:
聂永,新浪微博技术专家。从事开发多年,兴趣广泛,前后端都有涉及,包括 Web Page、HTTP API、GraphQL、Erlang、Lua、Golang、Linux Kernel、TCP Server、UDP/QUIC、运维和测试等,也乐此不疲。
工作中擅长写简洁文档,项目接口和 WIKI 基本上会同时展现给团队成员或协作的小伙伴,并努力让自己的行为透明化。
工作业余时间经常为提升团队成员工作效率而努力,为成员技能成长提供针对性建议和帮助。
个人喜好折腾,为公司贡献 5 项技术创新专利,经常写博客。
在即将于 4 月 20~22 日举行的 QCon 北京 2018 上,他将分享《QUIC 在手机微博中的应用实践》,详细解释 QUIC 在微博应用的经验。
评论