导语:QUIC (Quick UDP Internet Connection,快速UDP互联网连接) 是一个新的基于UDP的多路复用且安全的传输协议。它从头开始设计,完全弃用了TCP,使用UDP作为底层传输协议。QUIC提供了等价于HTTP/2 的多路复用和流控,等价于 TLS 的安全机制,及等价于 TCP的连接语义、可靠性和拥塞控制。据国际互联网工程任务组(The Internet Engineering Task Force,简称 IETF )消息,HTTP-over-QUIC 实验性协议将被重命名为 HTTP/3,并有望成为 HTTP 协议的第三个正式版本。
本文将介绍QUIC协议在CDN技术上,腾讯多媒体的应用落地,以及相比于TCP所带来的极快的优化体验,并尝试分析QUIC到底快在哪里?
1、背景
在现有物理资源的情况下,CDN海外机房回源、回传延迟奇高,RTT最高将近500ms,TCP丢包率和成功率表现不尽人意,一个大视频分片的上传下载需要到2秒级。而QUIC的特性在网络传输,尤其是弱网情况下,效果相比TCP好很多。多段256 NACK、更加激进的Loss Detection丢包探测和重传机制、以及0-RTT的握手建立,使其相比TCP的传输延迟大大降低。
2、封装QUIC Client SDK
QUIC运行在用户空间,底层使用UDP作为传输协议。客户端侧主要涉及QuicConnection、QuicStream、QuicSession三个核心功能类。
-
QuicConnection:负责管理QUIC连接,创建Connectionid,完成QUIC的握手(1-RTT/0-RTT),确保连接建立。
-
QuicSession:会话管理,管理QUIC整个会话的生命周期。包括QuicConnection、各种定时器Alarm、以及多Stream的生命周期等。
- QuicStream:QUIC真正进行数据传输的媒介,支持多个QuicStream并发传输,Stream之间的数据丢包重传等互不影响,真正消除TCP协议的队头阻塞缺陷。
QUIC协议在应用层实现了原本TCP几乎所有的特性:传输窗口、ACK、多路复用、Loss Detecion、流量控制、拥塞控制等等,非常复杂。所以在工程实现上我抽取了chromium的net库等,重载QuicStream、QuicSession等,封装成易于理解的QUIC Client SDK,提供quic_socket、quic_connect、quic_send、quic_recv等开发者比较习惯的socket使用方式API。
图1、QUIC Client SDK
3、QUIC Client在MCP++框架上的应用
MCP++框架主要是DCC模块负责对外的请求下载等,在CDN上的应用的话,Front节点的回源、回传都需要通过DCC去下载和上传。
- 1)、QUIC由于其复杂的特性实现,运行在应用层的协议栈,需要大量的CPU计算。
-
2)、以及QUIC Client自成一体的UDP Socket收发,DCC原本的epoll_wait单线程模型无法支持,很难改造。
故而决定采用 DCC线程 + QUIC线程 的模型支持QUIC协议,QUIC Client线程保持高度的自治,请求与响应皆自我完善。DCC原本的连接管理,CRawCache等需要自己实现。
图2、MCP DCC与QUIC交互图2所示,DCC与QUIC交互主要有两个流程,不破坏现有DCC框架:
- 1、send_mq_request_by_quic负责将来自mq的请求发给QUIC,由quic创建UDP socket发送出去。
- 2、handle_network_recive_quic负责从队列queue里取出QUIC接收完处理后的响应消息。
3、QUIC请求流程,以及失败回退到TCP机制
由于QUIC底层通过UDP进行连接,在大规模服务场景下,几万台机器所处不同的运营商和网路环境,无法保证各种中间设备UDP和端口都可用。所以在设计时不能完全依赖UDP可信,要考虑失败回退TCP机制。
DCC与QUIC的交互有send_mq_request_by_quic, handle_network_recive_quic发送和接收两种场景,分为发送失败和接收失败。
图3、QUIC请求与回退流程DCC->QUIC请求流程:
1. epoll_wait接收来自MCD的管道mq事件。
2. 判断请求QUIC or TCP,用QUIC 则send_mq_request_by_quic。
3. 判断当前要请求的flow连接是否已回退到TCP,已回退的话这里直接走TCP。
4. QUIC,GetQuicConn(flow)获取QUIC连接id,连接存在直接请求,不存在quic_connect()发起握手创建连接。
5. connect失败(直接返回失败,有些场景不会直接返回失败),flow连接回退到TCP(后续该连接其他消息30分钟内走TCP),当前请求消息通过TCP重试。
6. connect成功,quic_send()发送数据:
- a. 发送成功,SUCCESS流程结束,异步等待queue响应。
- b. 发送失败,flow连接回退到TCP,当前请求消息通过TCP重试。
- c. Stream busy,QUIC所有Stream都处在忙状态,连接不回退,只有当前请求使用tcp发送。
QUIC->DCC响应流程:
1、 QUIC接收校验完毕check_complete后,将响应消息入队列queue。
2、DCC线程send_mq_request_by_quic从队列Dequeue 取出响应消息。
- a). QUIC_SUCCESS,将响应消息enqueue 发给mcd。
- b). CONN_ERROR,连接中断,将quic传来的消息进行TCP重发,flow连接回退到TCP。
0-RTT场景下,quic_connect()发出去CLHO后client端即认为连接成功,调用quic_send()发送数据(数据缓存在QUIC底层buff), 实际握手可能会超时失败。OnConnectionClose 失败后需要通过queue通知DCC线程做相应的处理。
4、0-RTT
QUIC比TCP快的一个很大特点是其握手使用了更少的RTT,甚至达到0-RTT握手,直接进行数据传输,相比TCP大大减少了三次握手的等待时间。而我们在CDN上的应用更进一步,front->zone的分布式集群所有机器之间的请求,除了第一次,以后均可以实现0-RTT建链。
图5、QUIC 0-RTT背景:QUIC handshake握手阶段服务端REJ时会下发server_config和token给客户端。客户端下次握手时,如果携带上上次下发的server_config和token到服务端,服务端验证通过,即可实现0-RTT。
在当前的CDN架构上要实现0-RTT,有几个问题需要解决:
问题:
-
问题1. 服务端front有多个ccd进程,每个进程生成的server_config都不一样,客户端携带上次的server_config请求到其他ccd进程时,服务端校验不通过,无法0-RTT?
-
问题2. 客户端ip不是固定的,4G/WIFI切换时每次都会变换ip,服务端下发的token融合了客户端的ip,客户端的ip变化时,携带的token服务端校验不通过,无法0-RTT?
-
问题3. 服务端ip对于客户端来说不是固定的,服务端是一个集群,请求新的服务器ip时,如果当前client没有请求过该server_id,本地没有缓存过新server的cache(server_config等),就无法实现0-RTT。
解决方案:
-
解决1:服务端所有front集群固定生成同一个server_config_id。
-
解决2:服务端关闭token验证开关,QUIC提供关闭验证的开关,在应用层即可关闭。
- 解决3:客户端每次构建新的请求连接时使用相同的server_id(host,port)。Server_id是缓存在客户端本地,用来指引cache的key, 对于客户端来说,这相当于把对端所有server集群看成同一个server,对同一个server进行下次握手,就会直接发起full CLHO消息。服务端那边校验server_config通过,就能建立新的连接了。
5、QUIC对比TCP的优化效果
线上灰度观察效果,QUIC优化效果明显。尤其是在弱网下,传输时延大大降低:
a)、国内以长沙电信为例,回源延时219ms -> 88ms,优化效果能达到59.8%,回传原本延时就比较低了,延时优化效果也能达到25%。
b)、海外以美国点为例,RTT比较高的场景196ms,QUIC的回源回传效果更加明显1.3s->339ms,优化率达到73.9%。
6、QUIC快在哪里?
QUIC的提升效果表现如此的好,究竟比TCP快在什么地方呢?TCP协议经过几十年的发展,可以说构建了整个互联网的基础。Google为了开发QUIC做了很多工作,基本上把整个TCP的协议栈都重新实现了一遍(取其精华,修复缺陷)。尝试分析一下:
1. 0-RTT
TCP的三次握手带来必定的1-RTT消耗,在网络状况比较好的情况下,大部分的消耗可能都来自业务,不觉得RTT耗时很多。但是在网路状况差的情况下(比如上面的美国),1个RTT就带来196ms的延时,使用QUIC优化后,可以看出整体耗时只有339ms,所以0-RTT对于秒开业务来说是一个很重要的特性。
2. QUIC Stream多路复用 — TCP 队头阻塞
QUIC支持多路复用,QUIC的底层UDP不进行排序和确认,只负责传输,ACK、确认、排序等都在QUIC进行。TCP当有多个流同时传输时,如果某个流发生了丢包,其他流需要等待其重传排序完成才能继续传输,因为TCP的滑动窗口只有一个,队头阻塞无法避免。而QUIC的多个Stream之间重传、排序等互不干扰,有stream级别的流控窗口,真正从传输层解决队头阻塞的问题。
从客户端和CDN服务器的统计上看到,同一连接多个Stream同时传输的场景非常频繁。而去除了队头阻塞的QUIC延时比TCP低就很明显了,并且在弱网丢包率高场景下,表现更好。
3. Up to 256 NACK ranges,支持256分段ACK
TCP每次只会ACK一个数据包,当滑动窗口用尽之后,只能等待新的ACK到来才能继续发送新数据包。就算开启了SACK,最多也只能ACK三个包。
QUIC最大支持ACK 256个数据包,支持分段ACK。就算某个ACK包丢失,后续其他包的ACK也能带上之前ACK过的包号。
4. Loss Detection丢包探测
QUIC在TCP的经验之上重新构建了一种新的丢包探测机制:FR快重传、ER早期重传、RTO、TLP、F-RTO,和TCP相比很大不同。表现为更为激进的丢包发现和重传策略:比如如果收到最大的ACK包号大于3就判断丢包进行FR重传;而TCP需要收到连续三个重复的ACK才判断丢包进行快重传。
详细描述:QUIC协议研究(五):QUIC Loss Detection丢包探测 http://km.oa.com/group/15624/articles/show/397474
7、总结
本文介绍当前主流的基于chromium的QUIC Client SDK的架构,以及其在腾讯MCP++框架上的工程落地。详细介绍了MCP++框架上的 DCC+QUIC 多线程lib方案,和QUIC回退到TCP的保障机制,另外简单介绍了下全面 0-RTT 在 Front->zone 上实现存在的问题和解决方案。通过在线上使用QUIC与TCP的实际对比效果,以及QUIC多路复用、256NACK等优秀特性,国内各大厂商纷纷在研究和应用QUIC协议,证明QUIC是一个潜力很大的技术。IETF已经将HTTP over QUIC重命名为HTTP3,随着下一代HTTP3协议的普及,QUIC将应用的更加广泛。
转载请注明出处,商用请征得作者本人同意。
文章首发于公众号:【昭景成】,id:zhaojc100。
欢迎关注,BAT攻城狮,和你一起聊聊职场、聊聊技术、聊聊互联网上的那些事儿。
标签:QUIC,CDN,DCC,TCP,server,RTT,quic,加速 From: https://www.cnblogs.com/zhaojc100/p/16867980.html