HTTP3.0和QUIC协议那些事

HTTP3.0和QUIC协议那些事


在这里插入图片描述

写在前面

  • 如果你的 App,在不需要任何修改的情况下就能提升 15% 以上的访问速度。特别是弱网络的时候能够提升 20% 以上的访问速度。
  • 如果你的 App,在频繁切换 4G 和 WIFI 网络的情况下,不会断线,不需要重连,用户无任何感知。如果你的 App,既需要 TLS 的安全,也想实现 HTTP2 多路复用的强大。
  • 如果你刚刚才听说 HTTP2 是下一代互联网协议,如果你刚刚才关注到 TLS1.3 是一个革命性具有里程碑意义的协议,但是这两个协议却一直在被另一个更新兴的协议所影响和挑战。

一、HTTP2.0和HTTP3.0

  • 科技永不止步

  • 我们都知道互联网中业务是不断迭代前进的,像HTTP这种重要的网络协议也是如此,新版本是对旧版本的扬弃。

1.1 HTTP2.0和TCP的爱恨纠葛

  • HTTP2.0是2015年推出的,还是比较年轻的,其重要的二进制分帧协议、多路复用、头部压缩、服务端推送等重要优化使HTTP协议真正上了一个新台阶。

在这里插入图片描述

  • 像谷歌这种重要的公司并没有满足于此,而且想继续提升HTTP的性能,花最少的时间和资源获取极致体验。
  • 那么肯定要问HTTP2.0虽然性能已经不错了,还有什么不足吗? 建立连接时间长(本质上是TCP的问题) ,队头阻塞问题
  • 移动互联网领域表现不佳(弱网环境) …
  • 熟悉HTTP2.0协议的同学应该知道,这些缺点基本都是由于TCP协议引起的,水能载舟亦能覆舟,其实TCP也很无辜呀!

在这里插入图片描述

  • 在我们眼里,TCP是面向连接、可靠的传输层协议,当前几乎所有重要的协议和应用都是基于TCP来实现的。
  • 网络环境的改变速度很快,但是TCP协议相对缓慢,正是这种矛盾促使谷歌做出了一个看似出乎意料的决定-基于UDP来开发新一代HTTP协议

1.2 谷歌为什么选择UDP

上面提到,谷歌选择UDP是看似出乎意料的,仔细想一想其实很有道理。

  • 我们单纯地看看TCP协议的不足和UDP的一些优点:
  • 基于TCP开发的设备和协议非常多,兼容困难
  • TCP协议栈是Linux内部的重要部分,修改和升级成本很大
  • UDP本身是无连接的、没有建链和拆链成本
  • UDP的数据包无队头阻塞问题
  • UDP改造成本小

从上面的对比可以知道,谷歌要想从TCP上进行改造升级绝非易事,但是UDP虽然没有TCP为了保证可靠连接而引发的问题,但是UDP本身不可靠,又不能直接用

在这里插入图片描述

  • 综合而知,谷歌决定在UDP基础上改造一个具备TCP协议优点的新协议也就顺理成章了,这个新协议就是QUIC协议

1.3 QUIC协议和HTTP3.0

  • QUIC其实是Quick UDP Internet Connections的缩写,直译为快速UDP互联网连接。

在这里插入图片描述

  • 我们来看看维基百科对于QUIC协议的一些介绍:
  • QUIC协议最初由Google的Jim Roskind设计,实施并于2012年部署,在2013年随着实验的扩大而公开宣布,并向IETF进行了描述。
  • QUIC提高了当前正在使用TCP的面向连接的Web应用程序的性能。它在两个端点之间使用用户数据报协议(UDP)建立多个复用连接来实现此目的
  • QUIC的次要目标包括减少连接和传输延迟,在每个方向进行带宽估计以避免拥塞。它还将拥塞控制算法移动到用户空间,而不是内核空间,此外使用前向纠错(FEC)进行扩展,以在出现错误时进一步提高性能。
  • HTTP3.0又称为HTTP Over QUIC,其弃用TCP协议,改为使用基于UDP协议的QUIC协议来实现。
    在这里插入图片描述

二、QUIC详解

择其善者而从之,其不善者而改之。

  • HTTP3.0既然选择了QUIC协议,也就意味着HTTP3.0基本继承了HTTP2.0的强大功能,并且进一步解决了HTTP2.0存在的一些问题,同时必然引入了新的问题

在这里插入图片描述
QUIC协议必须要实现HTTP2.0在TCP协议上的重要功能,同时解决遗留问题,我们来看看QUIC是如何实现的。

2.1 队头阻塞问题

  • 队头阻塞 Head-of-line blocking(缩写为HOL blocking)是计算机网络中是一种性能受限的现象,通俗来说就是:一个数据包影响了一堆数据包,它不来大家都走不了

  • 队头阻塞问题可能存在于HTTP层和TCP层,在HTTP1.x时两个层次都存在该问题
    在这里插入图片描述

  • HTTP2.0协议的多路复用机制解决了HTTP层的队头阻塞问题,但是在TCP层仍然存在队头阻塞问题。

  • TCP协议在收到数据包之后,这部分数据可能是乱序到达的,但是TCP必须将所有数据收集排序整合后给上层使用,如果其中某个包丢失了,就必须等待重传,从而出现某个丢包数据阻塞整个连接的数据使用。

  • 多路复用是 HTTP2 最强大的特性 ,能够将多条请求在一条 TCP 连接上同时发出去。但也恶化了 TCP 的一个问题,队头阻塞 ,如下图示:
    在这里插入图片描述

  • HTTP2 在一个 TCP 连接上同时发送 4 个 Stream。其中 Stream1 已经正确到达,并被应用层读取

  • 但是 Stream2 的第三个 tcp segment 丢失了,TCP 为了保证数据的可靠性,需要发送端重传第 3 个 segment 才能通知应用层读取接下去的数据,虽然这个时候 Stream3 和 Stream4 的全部数据已经到达了接收端,但都被阻塞住了

  • 不仅如此,由于 HTTP2 强制使用 TLS,还存在一个 TLS 协议层面的队头阻塞
    在这里插入图片描述

  • QUIC 的多路复用和 HTTP2 类似。在一条 QUIC 连接上可以并发发送多个 HTTP 请求 (stream)。但是 QUIC 的多路复用相比 HTTP2 有一个很大的优势

  • QUIC 一个连接上的多个 stream 之间没有依赖。这样假如 stream2 丢了一个 udp packet,也只会影响 stream2 的处理。不会影响 stream2 之前及之后的 stream 的处理

  • 这也就在很大程度上缓解甚至消除了队头阻塞的影响。

  • QUIC协议是基于UDP协议实现的,在一条链接上可以有多个流,流与流之间是互不影响的,当一个流出现丢包影响范围非常小,从而解决队头阻塞问题

2.2 0RTT 建链

  • 衡量网络建链的常用指标是RTT Round-Trip Time,也就是数据包一来一回的时间消耗。
    在这里插入图片描述

  • RTT包括三部分:往返传播时延、网络设备内排队时延、应用程序数据处理时延。
    在这里插入图片描述

  • 一般来说HTTPS协议要建立完整链接包括:TCP握手和TLS握手,总计需要至少2-3个RTT,普通的HTTP协议也需要至少1个RTT才可以完成握手。

  • 然而,QUIC协议可以实现在第一个包就可以包含有效的应用数据,从而实现0RTT,但这也是有条件的。

  • 0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。那什么是 0RTT 建连呢?这里面有两层含义。

  • 传输层 0RTT 就能建立连接

  • 加密层 0RTT 就能建立加密连接
    在这里插入图片描述

  • 比如上图左边是 HTTPS 的一次完全握手的建连过程,需要 3 个 RTT。就算是 Session Resumption,也需要至少 2 个 RTT。
  • 而 QUIC 呢?由于建立在 UDP 的基础上,同时又实现了 0RTT 的安全握手,所以在大部分情况下,只需要 0 个 RTT 就能实现数据发送,在实现前向加密 的基础上,并且 0RTT 的成功率相比 TLS 的 Sesison Ticket 要高很多。
  • 简单来说,基于TCP协议和TLS协议的HTTP2.0在真正发送数据包之前需要花费一些时间来完成握手和加密协商,完成之后才可以真正传输业务数据
  • 但是QUIC则第一个数据包就可以发业务数据,从而在连接延时有很大优势,可以节约数百毫秒的时间。

在这里插入图片描述

  • QUIC的0RTT也是需要条件的,对于第一次交互的客户端和服务端0RTT也是做不到的,毕竟双方完全陌生。
  • 因此,QUIC协议可以分为首次连接和非首次连接,两种情况进行讨论。

2.2.1 首次连接和非首次连接

  • 使用QUIC协议的客户端和服务端要使用1RTT进行密钥交换,使用的交换算法是DH(Diffie-Hellman)迪菲-赫尔曼算法。

2.2.2 首次连接

  • 简单来说一下,首次连接时客户端和服务端的密钥协商和数据传输过程,其中涉及了DH算法的基本过程:
    在这里插入图片描述
    在这里插入图片描述

2.2.3 非首次连接

  • 前面提到客户端和服务端首次连接时服务端传递了config包,里面包含了服务端公钥和两个随机数,客户端会将config存储下来,后续再连接时可以直接使用,从而跳过这个1RTT,实现0RTT的业务数据交互
  • 客户端保存config是有时间期限的,在config失效之后仍然需要进行首次连接时的密钥交换。

2.3 前向安全问题

前向安全是密码学领域的专业术语,看下百度上的解释:

  • 前向安全或前向保密Forward Secrecy是密码学中通讯协议的安全属性,指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏
  • 前向安全能够保护过去进行的通讯不受密码或密钥在未来暴露的威胁,如果系统具有前向安全性,就可以保证在主密钥泄露时历史通讯的安全,即使系统遭到主动攻击也是如此

通俗来说,前向安全指的是密钥泄漏也不会让之前加密的数据被泄漏,影响的只有当前,对之前的数据无影响

在这里插入图片描述
在这里插入图片描述

2.4 前向纠错

  • 前向纠错是通信领域的术语,看下百科的解释:
  • 前向纠错也叫前向纠错码Forward Error Correction 简称FEC;是增加数据通讯可信度的方法,在单向通讯信道中,一旦错误被发现,其接收器将无权再请求传输
  • FEC是利用数据进行传输冗余信息的方法,当传输中出现错误,将允许接收器再建数据。
  • 听这段描述就是做校验的,看看QUIC协议是如何实现的:
  • QUIC每发送一组数据就对这组数据进行异或运算,并将结果作为一个FEC包发送出去,接收方收到这一组数据后根据数据包和FEC包即可进行校验和纠错

2.5 连接迁移

网络切换几乎无时无刻不在发生。

  • TCP协议使用五元组来表示一条唯一的连接,当我们从4G环境切换到wifi环境时,手机的IP地址就会发生变化,这时必须创建新的TCP连接才能继续传输数据
  • QUIC协议基于UDP实现摒弃了五元组的概念,使用64位的随机数作为连接的ID,并使用该ID表示连接。
  • 基于QUIC协议之下,我们在日常wifi和4G切换时,或者不同基站之间切换都不会重连,从而提高业务层的体验。

在这里插入图片描述

2.6 改进的拥塞控制

  • TCP 的拥塞控制实际上包含了四个算法:慢启动,拥塞避免,快速重传,快速恢复

  • QUIC 协议当前默认使用了 TCP 协议的 Cubic 拥塞控制算法 ,同时也支持 CubicBytes, Reno, RenoBytes, BBR, PCC 等拥塞控制算法。

从拥塞算法本身来看,QUIC 只是按照 TCP 协议重新实现了一遍,那么 QUIC 协议到底改进在哪些方面呢?主要有如下几点:

2.6.1 可插拔

  • 什么叫可插拔呢?就是能够非常灵活地生效,变更和停止,体现在如下方面:
  • 应用程序层面就能实现不同的拥塞控制算法,不需要操作系统,不需要内核支持。这是一个飞跃,因为传统的 TCP 拥塞控制,必须要端到端的网络协议栈支持,才能实现控制效果。而内核和操作系统的部署成本非常高,升级周期很长,这在产品快速迭代,网络爆炸式增长的今天,显然有点满足不了需求。
  • 即使是单个应用程序的不同连接也能支持配置不同的拥塞控制。就算是一台服务器,接入的用户网络环境也千差万别,结合大数据及人工智能处理,我们能为各个用户提供不同的但又更加精准更加有效的拥塞控制。比如 BBR 适合,Cubic 适合。
  • 应用程序不需要停机和升级就能实现拥塞控制的变更,我们在服务端只需要修改一下配置,reload 一下,完全不需要停止服务就能实现拥塞控制的切换。STGW 在配置层面进行了优化,我们可以针对不同业务,不同网络制式,甚至不同的 RTT,使用不同的拥塞控制算法。

2.6.2单调递增的 Packet Number

  • TCP 为了保证可靠性,使用了基于字节序号的 Sequence Number 及 Ack 来确认消息的有序到达。
  • QUIC 同样是一个可靠的协议,它使用 Packet Number 代替了 TCP 的 sequence number,并且每个 Packet Number 都严格递增,也就是说就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值
  • 而 TCP 呢,重传 segment 的 sequence number 和原始的 segment 的 Sequence Number 保持不变,也正是由于这个特性,引入了 Tcp 重传的歧义问题

在这里插入图片描述

  • 如上图所示,超时事件 RTO 发生后,客户端发起重传,然后接收到了 Ack 数据。由于序列号一样,这个 Ack 数据到底是原始请求的响应还是重传请求的响应呢?不好判断

  • 如果算成原始请求的响应,但实际上是重传请求的响应(上图左),会导致采样 RTT 变大。如果算成重传请求的响应,但实际上是原始请求的响应,又很容易导致采样 RTT 过小

  • 由于 Quic 重传的 Packet 和原始 Packet 的 Pakcet Number 是严格递增的,所以很容易就解决了这个问题。

在这里插入图片描述

  • 如上图所示,RTO 发生后,根据重传的 Packet Number 就能确定精确的 RTT 计算。如果 Ack 的 Packet Number 是 N+M,就根据重传请求计算采样 RTT。如果 Ack 的 Pakcet Number 是 N,就根据原始请求的时间计算采样 RTT,没有歧义性

  • 但是单纯依靠严格递增的 Packet Number 肯定是无法保证数据的顺序性和可靠性。QUIC 又引入了一个 Stream Offset 的概念。

在这里插入图片描述

  • 一个 Stream 可以经过多个 Packet 传输,Packet Number 严格递增,没有依赖
  • Packet 里的 Payload 如果是 Stream 的话,就需要依靠 Stream 的 Offset
    来保证应用数据的顺序。
  • 如图所示,发送端先后发送了 Pakcet N 和 Pakcet N+1,Stream 的Offset 分别是 x 和 x+y。
  • 假设 Packet N 丢失了,发起重传,重传的 Packet Number 是 N+2,但是它的 Stream 的 Offset 依然是 x,这样就算 Packet N + 2 是后到的,依然可以将 Stream x 和 Stream x+y 按照顺序组织起来,交给应用程序处理。

2.6.3 不允许 Reneging

  • 什么叫 Reneging 呢?就是接收方丢弃已经接收并且上报给 SACK 选项的内容 。TCP 协议不鼓励这种行为,但是协议层面允许这样的行为。主要是考虑到服务器资源有限,比如 Buffer 溢出,内存不够等情况。

  • Reneging 对数据重传会产生很大的干扰。因为 Sack 都已经表明接收到了,但是接收端事实上丢弃了该数据

  • QUIC 在协议层面禁止 Reneging,一个 Packet 只要被 Ack,就认为它一定被正确接收,减少了这种干扰

2.6.4 更多的 Ack 块

  • TCP 的 Sack 选项能够告诉发送方已经接收到的连续 Segment 的范围,方便发送方进行选择性重传。

  • 由于 TCP 头部最大只有 60 个字节,标准头部占用了 20 字节,所以 Tcp Option 最大长度只有 40 字节,再加上 Tcp Timestamp option 占用了 10 个字节 ,所以留给 Sack 选项的只有 30 个字节

  • 每一个 Sack Block 的长度是 8 个,加上 Sack Option 头部 2 个字节,也就意味着 Tcp Sack Option 最大只能提供 3 个 Block

  • 但是 Quic Ack Frame 可以同时提供 256 个 Ack Block,在丢包率比较高的网络下,更多的 Sack Block 可以提升网络的恢复速度,减少重传量

2.6.5 Ack Delay 时间

  • Tcp 的 Timestamp 选项存在一个问题 ,它只是回显了发送方的时间戳,但是没有计算接收端接收到 segment 到发送 Ack 该 segment 的时间。这个时间可以简称为 Ack Delay。

  • 这样就会导致 RTT 计算误差。如下图:

在这里插入图片描述

  • 可以认为 TCP 的 RTT 计算:
    在这里插入图片描述
  • 而 Quic 计算如下:
    在这里插入图片描述
  • 当然 RTT 的具体计算没有这么简单,需要采样,参考历史数值进行平滑计算,参考如下公式:
    在这里插入图片描述

2.6.6 基于 stream 和 connecton 级别的流量控制

  • QUIC 的流量控制 类似 HTTP2,即在 Connection 和 Stream 级别提供了两种流量控制。为什么需要两类流量控制呢?主要是因为 QUIC 支持多路复用。

  • Stream 可以认为就是一条 HTTP 请求。

  • Connection 可以类比一条 TCP 连接。

  • 多路复用意味着在一条 Connetion 上会同时存在多条 Stream。既需要对单个 Stream 进行控制,又需要针对所有 Stream 进行总体控制。

QUIC 实现流量控制的原理比较简单:

  • 通过 window_update 帧告诉对端自己可以接收的字节数,这样发送方就不会发送超过这个数量的数据
  • 通过 BlockFrame 告诉对端由于流量控制被阻塞了,无法发送数据
  • QUIC 的流量控制和 TCP 有点区别,TCP 为了保证可靠性,窗口左边沿向右滑动时的长度取决于已经确认的字节数。如果中间出现丢包,就算接收到了更大序号的 Segment,窗口也无法超过这个序列号
  • 但 QUIC 不同,就算此前有些 packet 没有接收到,它的滑动只取决于接收到的最大偏移字节数。

在这里插入图片描述

  • 针对 Stream:
    在这里插入图片描述
  • 针对 Connection:
    在这里插入图片描述
    同样地,STGW 也在连接和 Stream 级别设置了不同的窗口数。

最重要的是,我们可以在内存不足或者上游处理性能出现问题时,通过流量控制来限制传输速率,保障服务可用性。

三、总结

QUIC协议 存在的意义在于解决 TCP 协议的一些无法解决的痛点

  • 多次握手:TCP 协议需要三次握手建立连接,而如果需要 TLS 证书的交换,那么则需要更多次的握手才能建立可靠连接,这在如今长肥网络的趋势下是一个巨大的痛点
  • 队头阻塞:TCP 协议下,如果出现丢包,则一条连接将一直被阻塞等待该包的重传,即使后来的数据包可以被缓存,但也无法被递交给应用层去处理。
  • 无法判断一个 ACK 是重传包的 ACK 还是原本包的 ACK:比如 一个包 seq=1, 超时重传的包同样是 seq=1,这样在收到一个 ack=1 之后,我们无法判断这个 ack 是对之前的包的 ack 还是对重传包的 ack,这会导致我们对 RTT 的估计出现误差,无法提供更准确的拥塞控制
  • 无法进行连接迁移一条连接由一个四元组标识,在当今移动互联网的时代,如果一台手机从一个 wifi 环境切换到另一个 wifi 环境,ip 发生变化,那么连接必须重新建立,inflight 的包全部丢失。

现在我们给出一个 QUIC 协议的 Overview

  • 更好的连接建立方式
  • 更好的拥塞控制
  • 没有队头阻塞的多路复用
  • 前向纠错
  • 连接迁移
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页