HTTP/3
HTTP/3
QUIC
HTTP/3 同 HTTP/2 一样采用二进制帧的结构,不同的地方在于 HTTP/2 的二进制帧里需要定义 Stream,而 HTTP/3 自身不需要再定义 Stream,直接使用 QUIC 里的 Stream,于是 HTTP/3 的帧的结构也变简单了。

从上图可以看到,HTTP/3 帧头只有两个字段:类型和长度。
根据帧类型的不同,大体上分为数据帧和控制帧两大类,Headers 帧(HTTP 头部)和 DATA 帧(HTTP 包体)属于数据帧。
HTTP/3 在头部压缩算法这一方面也做了升级,升级成了 QPACK。与 HTTP/2 中的 HPACK 编码方式相似,HTTP/3 中的 QPACK 也采用了静态表、动态表及 Huffman 编码。
对于静态表的变化,HTTP/2 中的 HPACK 的静态表只有 61 项,而 HTTP/3 中的 QPACK 的静态表扩大到 91 项。
HTTP/2 和 HTTP/3 的 Huffman 编码并没有多大不同,但是动态表编解码方式不同。
所谓的动态表,在首次请求-响应后,双方会将未包含在静态表中的 Header 项更新各自的动态表,接着后续传输时仅用 1 个数字表示,然后对方可以根据这 1 个数字从动态表查到对应的数据,就不必每次都传输长长的数据,大大提升了编码效率。
可以看到,动态表是具有时序性的,如果首次出现的请求发生了丢包,后续的收到请求,对方就无法解码出 HPACK 头部,因为对方还没建立好动态表,因此后续的请求解码会阻塞到首次请求中丢失的数据包重传过来。
HTTP/3 的 QPACK 解决了这一问题,那它是如何解决的呢?
QUIC 会有两个特殊的单向流,所谓的单向流只有一端可以发送消息,双向则指两端都可以发送消息,传输 HTTP 消息时用的是双向流,这两个单向流的用法:
- 一个叫 QPACK Encoder Stream,用于将一个字典(Key-Value)传递给对方,比如面对不属于静态表的 HTTP 请求头部,客户端可以通过这个 Stream 发送字典;
- 一个叫 QPACK Decoder Stream,用于响应对方,告诉它刚发的字典已经更新到自己的本地动态表了,后续就可以使用这个字典来编码了。
这两个特殊的单向流是用来同步双方的动态表,编码方收到解码方更新确认的通知后,才使用动态表编码 HTTP 头部。
通过前文的介绍,相信大家对 HTTP3 已经有了一个初步的了解。总的来说,HTTP3 协议使用的 QUIC 提供的多路复用提高了传输效率,而本身并没有更改 HTTP 的语义。
HTTP3 与 HTTP2 一样,采用二进制、静态表、动态表与 Huffman 算法对 HTTP Header 编码,不只提供了高压缩率,还加快了发送端编码、接收端解码的速度。不过,由于 HTTP1 协议不支持多路复用,这样高并发只能通过多开一些 TCP 连接实现。因此,HTTP2 与 HTTP3 都在应用层实现了多路复用功能。
可以看到,相比 HTTP2,HTTP3 对传输层和表示层进行了重新改造,改造后在多路复用后,丢包阻塞的问题得到了解决,虽然某个包丢失了,但是并不会影响其他包的传递。
总的来说,HTTP3 创造出 Connection ID 概念实现了连接迁移,通过融合传输层、表示层,既缩短了握手时长,也加密了传输层中的绝大部分字段,提升了网络安全性。
同时,HTTP3 在 Packet 层保障了连接的可靠性,在 QUIC Frame 层实现了有序字节流,在 HTTP3 Frame 层实现了 HTTP 语义,这彻底解开了队头阻塞问题,真正实现了应用层的多路复用。