HTTP3.0(QUIC)的改进优势

参考文档:https://blog.csdn.net/wolfGuiDao/article/details/108729560

HTTP2.0 的优势

二进制分帧

先来理解几个概念:

:HTTP/2 数据通信的最小单位消息:指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等,消息由一个或多个帧组成。

:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID。

HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 采用二进制编码,将请求和响应数据分割为更小的帧。其中首部信息就是HEADER Frame,而body信息就是DATA Frame

HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。

多路复用

在 HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且为了控制资源,有时候还会对单个域名有 6-8个的TCP链接请求限制。

而在 HTTP/2 中,有了二进制分帧之后,不再依赖 TCP 链接去实现多流并行了,

    • 同域名下所有通信都在单个连接上完成。
    • 单个连接可以承载任意数量的双向数据流。
    • 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。

这降低了 TCP 连接建立和释放的开销,同时提高了带宽的利用率。尤其是后续的请求,没有TCP 的三次握手和慢启动过程,可以直接使用之前连接的高传输速率

还可以对不同的 stream 设置不同的优先级,stream 之间也可以设置依赖,依赖和优先级都可以动态调整

服务器推送

服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。

服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。

头部压缩

HTTP 1.1请求的大小变得越来越大,有时甚至会大于TCP窗口的初始大小,因为它们需要等待带着ACK的响应回来以后才能继续被发送。HTTP/2对消息头采用HPACK(专为http/2头部设计的压缩格式)进行压缩传输,能够节省消息头占用的网络的流量。而HTTP/1.x每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。 压缩技术的本质:字典表

HTTP2.0的劣势

  • 建立连接时间长(本质上是TCP的问题)
  • 多路复用解决了HTTP应用层的队头阻塞问题,但TCP链路层的队头阻塞问题依然存在。在 HTTP 2.0 中,多个请求是在同一个 TCP 管道中,这样当出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该 TCP连接中的所有请求。即使这些请求之间可能并不关联。
  • HTTP2.0,在接收端进行帧组装时,依旧要求同一 stream id 的数据是有序的。无法做到真正的乱序传输。
  • 移动互联网领域表现不佳(弱网环境下,4G/Wifi 切换的情况等)

HTTP3.0

HTTP3.0,也称作HTTP over QUIC。HTTP3.0的核心是QUIC协议,由Google在 2015年提出的SPDY v3演化而来的新协议,传统的HTTP协议是基于传输层TCP的协议,而QUIC是基于传输层UDP上的协议,

谷歌为什么选择UDP

我们单纯地看看TCP协议的不足和UDP的一些优点:

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

基于此,改造UDP协议而成的QUIC(Quick UDP Internet Connections)直译为快速UDP互联网连接,是非常好的选择,来作为HTTP3.0 的基石。

单调递增的 Packet Number

TCP 为了保证可靠性,重传包的包序号和原始的包序号是一致的,这就会导致Tcp 重传的歧义问题。

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

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


QUIC 同样是一个可靠的协议,它使用 Packet Number 代替了 TCP 的 sequence number,并且每个 Packet Number 都严格递增,也就是说就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值。

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

通过单调递增的packet number 来解决TCP的重传歧义问题,又通过stream offset 来确定这个包在这个stream 中的绝对位置,保证了数据的可靠性。

队头阻塞问题的解决

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

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

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

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

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

更多的 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 可以提升网络的恢复速度,减少重传量。

前向安全问题 & 0 RTT 优化

https://blog.csdn.net/wolfGuiDao/article/details/108729560

流量控制

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

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

 



posted @ 2022-07-05 15:10  Clovran-Wong  阅读(1301)  评论(3编辑  收藏  举报