zoukankan      html  css  js  c++  java
  • QUIC协议详解

    笔记 腾讯工程技术直播-QUIC协议详解

    一、QUIC简介

    QUIC介绍

    QUIC是Google开发的基于UDP的传输协议,用于提升网络加载速度。
    QUIC发展:2012年部署上线,2013年提交IETF,2021年推出标准RFC9000.

    QUIC 协议非常复杂,因为它做了太多事情:
    为了实现传输的可靠性,它基本上实现并且改进了整个 TCP 协议的功能,包括序列号,重传,拥塞控制,流量控制等。
    为了实现传输的安全性,它又彻底重构了 TLS 协议,包括证书压缩,握手消息,0RTT 等。虽然后续可能会采用 TLS1.3 协议,但是事实上是 QUIC 推动了 TLS1.3 的发展。
    为了实现传输的并发性,它又实现了 HTTP2 的大部分特性,包括多路复用,流量控制等。

    Google现网的QUIC数据

    • PC端:搜索和视频的传输延迟降低了8%
    • 移动端:搜索降低了6%,视频降低了5.3%

    还能发现,在弱网环境下,提升更加明显。

    QUIC是如何提升速度的呢?

    二、QUIC的数据包格式

    QUIC的数据包分为Header和Data部分,其中Header是明文传输,包括Flags是标志位,Connection ID是连接ID,可用于连接迁移,QUIC Version是QUIC的版本号,Packet Number是包序号,用于保证可靠传输;Data部分是密文传输,是一些数据帧,有很多数据帧类型:Stream、ACK、Padding、Blocked等,其中Stream帧传输应用数据

    Stream

    Frame Type: Bit7~Bit0

    1. Bit7:必须设置为1,表示Stream帧
    2. Bit6:如果设置为1,表示发送端在这个stream上已经结束发送数据,流将处于半关闭状态
    3. Bit5:如果设置为1,表示Stream头中包含Data Length字段
    4. Bit432:表示offset的长度。000表示0字节,001表示1字节,002表示2字节,以此类推
    5. Bit10:表示Stream ID的长度。00表示1字节,01表示2字节,10表示3字节,11表示4字节

    三、QUIC的实现原理

    1. 建立连接

    建立https连接

    先分析一样https的握手过程,包括TCP握手和TLS(Transport Layer Security)握手

    https连接耗时3个RTT

    QUIC基于TLS建立连接

    右边展示的是ECDH算法,一个RTT就可以协商好通信秘钥。

    0-RTT建立连接

    还可以0-RTT建立连接,原理在于:客户端缓存服务端配置ServerConfig(\(B = b*G%P\))
    ClientHello: \(A = c*G%P\)
    ServerHello: \(B = b*G%P\)
    通信秘钥:\(key = B*c = A*b = b*c*G%P\)
    客户端使用\(key\)发送数据,无需建立连接

    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能保证前向安全,什么是前向安全呢?
    问题:假设攻击者记录所有的通信数据和公开参数,一旦服务器的私钥\(b\)泄露,则计算计算出通信秘钥,这样就可以破解之前的通信数据了
    前向安全就是指用来产生会话秘钥的长期秘钥泄露出去,不会泄露以前的通讯内容

    总结起来就是,通过ServerConfig实现0-RTT握手,通过会话秘钥保证数据的前向安全

    2. 可靠传输

    QUIC是基于UDP的协议,而UDP是不可靠传输,QUIC如何实现可靠传输呢?
    可靠传输必须满足两个条件

    • 数据完整性:发送端发出的数据包,接收端都能收到
    • 数据有序性:接收端能按序组装数据包,解码得到原始数据

    可靠传输:数据完整性

    实现方案:基于包号PKN和确认应答SACK的丢包重传机制

    PKN是单调递增的,即使是重传,PKN也和之前的不一样。那么接收端怎么保证数据的有序性呢?
    通过添加数据包在原始数据中的偏移量offset,接收端根据offset字段对异步到达的数据包进行排序

    3. 流量控制

    就是说发送端发出的包,接收端要有足够的缓冲空间来接收。
    和TCP一样,QUIC也是利用滑动窗口机制实现流量控制,也就是连续ARQ协议

    如果发送端收到了接收端的ACK帧,窗口就会向右移动,可用窗口就会变大,然后发送新的数据包。

    虽然都是采用滑动窗口机制,和TCP不同的是,QUIC的滑动窗口分为Connect和Stream两种级别。

    • Connnect流量控制:规定了所有数据流的总窗口大小
    • Stream流量控制:规定了每个流的窗口大小

    因为QUIC中每个连接上可以发送多个请求,每个请求对应一条流,每个流有自己的滑动窗口,整个连接也有一个滑动窗口,其大小是所有流的可用窗口之和。

    4. 拥塞控制

    目的:通过拥塞窗口限制发送方的数据量,避免整个网络拥塞。

    它们的目的是不同的,流量控制是为了接防止发送太快,接收方接受不过来,拥塞控制控制是为了防止发送太快,整个网络拥塞。
    发送窗口的大小等于min(接受窗口,拥塞窗口),即 \(swnd = min(rwnd, cwnd)\)
    前面讲了接收窗口的大小是接收端告诉发送端的,那拥塞窗口的大小怎么得到的呢?
    很显然,拥塞窗口是为了避免网络拥塞,应该根据网络拥塞情况动态调整。那怎么判断网络的拥塞程度呢?例如发生超时,或连续收到三次相同的ACK等,分为基于丢包的和基于网络带宽的。

    拥塞控制:慢启动

    初始化:cwnd=1,也就是一个MDS(Max Datagram Size)数据包大小
    对于UDP而言:MDS = 1500(MTU)- 20(IP头部) - 8(UDP头部) = 1472字节,MTU是指网卡允许的最大包长度。

    通过查看QUIC的源码,发现初始阈值是2000倍的MDS。每收到2倍的ACK,拥塞窗口就增加到2倍。

    拥塞控制:拥塞避免

    每收到1个ACK,拥塞窗口就增加1。线性增长,一直增长,直达发生丢包。

    拥塞控制:拥塞发生

    超时重传,是有一个定时器,在指定时间内没有收到ACK,就会触发超时重传,并进入慢启动阶段;

    快速重传,是指发送端连续收到3个相同的ACK,就会触发快速重传,并进入快速恢复阶段。

    图中有点小错误,cwnd是ssthresh+3,如果是收到n个相同的ACK,则是ssthreash+n。

    5. 多路复用

    前面有提到QUIC的一大优势就是解决了队头阻塞的问题,什么叫队头阻塞,先回顾一个HTTP2协议。

    HTTP2

    HTTP2首次提出了多路复用,多路复用是指单个HTTP连接上可以同时发送多个HTTP请求,解决了HTTP1.1中单个连接只能发送一个请求的性能瓶颈。
    之所以能多路复用,是因为HTTP2和HTTP1.1的帧格式是不同的,其中StreamID就是流的ID,每个请求都是自己的流ID。

    • 1个请求对应1条流
    • 通过Stream ID判断数据帧属于哪个请求

    假设有A和B两个请求,对应的Stream ID分别为1和2

    在同一个TCP连接中,可以存在两个流。但是对于TCP连接来说,仍然只有1个滑动窗口来发送这些数据包。所以这就会有队头阻塞问题。什么是队头阻塞呢?

    队头阻塞

    客户端将56789发送出去,服务端返回了6789的ACK,但是ACK 5丢失了,会导致发送窗口无法向前移动。也就是说队头5阻塞了后面数据包的发送。

    多路复用:无队头阻塞

    实现原理:给每个流都分配一个滑动窗口

    虽然单条连接上无队头阻塞,但是单条流仍然存在队头阻塞。这也很容易理解,单条流还是一个滑动窗口。

    6. 连接迁移

    QUIC还有一个独特的优势:连接迁移,其他协议都没有这个特性。
    定义:当客户端切换网络时,和服务端的连接并不会断开(逻辑上),仍然可以正常通信。
    实现原理:QUIC的连接是基于64位的Connection ID,网络切换时并不会改变Connection ID,连接在逻辑上仍然是通的。
    TCP是不行的,TCP是基于四元组(客户端IP,客户端Port,服务端IP,服务端端口Port),只要有一个变化连接就会断开,必须要重新建立连接。

    四、总结

    问题:QUIC是如何提升网络加载速度的?

    1. 降低连接耗时:在客户端有缓存的情况下实现0-RTT建立连接
    2. 更灵活的拥塞控制:在用户态可以为每个请求配置不同的拥塞控制策略
    3. 无队头阻塞的多路复用:每个请求流独立拥有滑动窗口,互不影响
    4. 连接迁移:网络切换不会中断数据传输

    参考链接

    1. 「直播回放」腾讯工程师分享:QUIC协议详解
    2. 科普:QUIC协议原理分析
    3. 让互联网更快的协议,QUIC在腾讯的实践及性能优化
    4. 扒一扒能加速互联网的QUIC协议
    个性签名:时间会解决一切
  • 相关阅读:
    HBASE数据模型&扩展和负载均衡理论
    JVM Safepoint 安全点
    JVM垃圾回收算法 及 垃圾收集器
    JVM运行时数据区域
    Java IO、BIO、NIO、BIO
    Java反射
    Zookeeper
    二叉树深度
    二叉树层次遍历
    KMP算法
  • 原文地址:https://www.cnblogs.com/lfri/p/quic.html
Copyright © 2011-2022 走看看