什么是 HTTP/3?
要理解什么是 HTTP/3,需要明白 HTTP/3 的提出为了解决哪些问题,通过什么手段解决。
因此,先回顾历代 HTTP 协议的改进:
HTTP/1.0
HTTP/1.0 作为开山鼻祖,定义了 HTTP 协议的标准。但 HTTP/1.0 是一种无状态、无连接的应用层协议,其无连接的特性导致无法进行多路复用:每次发送请求,都需要进行一次新的 TCP 连接。
同时,存在队头阻塞问题:一个请求被阻塞将导致整个传输被阻塞。
HTTP/1.1
长连接(Keep-Alive):增加了一个 Connection 字段,通过设置Keep-Alive可以保持HTTP连接不断开,从而让客户端在一个 TCP 连接上发送多个请求,从而减少频繁握手的开销。
初步解决队头阻塞:后续请求无需等待前一个请求完成,可以直接继续发送。解决了客户端->服务器方向的阻塞问题。
但 HTTP/1.1 依旧存在以下问题:
- 没有彻底解决队头阻塞:服务器需要按请求顺序返回,也就是存在一个时间点,客户端需要等待服务器按顺序返回所有响应才能继续工作,依旧存在队头阻塞。
- 缺乏头部压缩机制:每次请求都会重复发送大量头信息。
HTTP/2
头部压缩:采用 HPACK 头压缩算法减少头部冗余。
多路复用:通过将数据拆分为帧并附加流标识,HTTP/2 实现了一个 TCP 连接上并发传输多个请求-响应流。进一步解决了服务器->客户端的阻塞问题。
二进制格式:全面采用二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧和数据帧。从而加速报文解析工作。
但依旧存在以下问题:
- 连接时间长:随着 TLS 的大规模应用,TCP 建立后往往会接着建立 TLS 连接。但 TCP 和 TLS 各自分别需要握手,从而增加了首次通信的延迟。
- 多路复用不彻底:工作在 TCP 上的多路复用导致 TCP 丢包阻塞时,所有 HTTP/2 的流都将阻塞。
HTTP/3
放弃了 TCP,而是基于 UDP+QUIC 实现传输层。同时,内置 TLS1.3 加密,将加密握手和传输握手合并以减少首次通信握手延迟。
当然 HTTP/3 依旧有自己的缺陷:QUIC/UDP 的加密传输难以被传统网络设备识别,甚至一些防火墙或运营商可能阻断或限速 UDP 流量。
一个常见的误解是:在 UDP 上实现可靠传输,不就是复现一个 TCP 吗?为什么会更快?
接下来的内容,希望能够解答这个问题。
为什么 UDP 上建立的可靠信道比 TCP 更快?
QUIC 作为 HTTP/3 的基础,相较于传统 TCP 有两大关键改进:
- 1-RTT 优化
- 阻塞优化
QUIC 可以用来承载多种应用协议(例如 DNS、SSH),但 HTTP/3 专指“HTTP over QUIC”。如果你提到 HTTP/3,几乎总是意味着它基于 QUIC。
1-RTT
传统 TCP+TLS
首先,为了建立可靠信道 TCP 需要进行三次握手,从而同步 SYN 以便通信双方交换各自的ACK。然而,在 TLS 广泛应用的今天,在 TCP 建立完成后往往还需要 TLS 在进行一次单独的握手以协商加密参数。以上步骤将花费2-3 个 RTT才能正式开始通信。
TCP 三次握手:
- 客户端 → SYN → 服务器;
- 服务器 → SYN-ACK → 客户端;
- 客户端 → ACK → 服务器。
—— 约 1 RTT
TLS 握手:
- TCP 建好后,客户端 → TLS ClientHello → 服务器;
- 服务器 → TLS ServerHello + 证书 + ServerDone → 客户端;
- 客户端 → TLS Finish → 服务器。
—— 约 1–2 RTT
初始连接总耗时大约 2–3 RTT,在高延迟网络中,性能影响很明显。
QUIC 建立可靠信道
1-RTT 首次连接机制:
QUIC 将 TCP 握手和 TLS 协商合并为一次握手:
- 客户端发出 ClientHello+QUIC Initial 包;
- 服务器回写 ServerHello、初始密钥 + 应用数据许可;
双方即可开始加密通信。以上过程仅需要 1 RTT 完成握手,客户端即可发应用数据。
0-RTT 重连:当客户端之前连接过同一服务器且已缓存在本地密钥,下一次重连可以直接发送 0-RTT 数据包,在 0 RTT 中完成连接——几乎立即开始传输数据 。
消除队头阻塞(Head‑of‑Line Blocking)
HTTP2 的多路复用机制
HTTP/2多路复用是指在同一个TCP连接上,客户端和服务器可以同时发送多个HTTP请求和响应,而无需按顺序等待每个请求完成。
HTTP2 如何解决队头阻塞:传统HTTP/1.1需要按顺序处理请求,而HTTP/2通过为每个请求/响应被分配唯一的流ID,从而获得区分乱序到达的 HTTP2 帧的能力,进而实现重组与并行处理。
与 TCP 多路复用的关键区别:
- 传输层多路复用(如TCP/UDP)基于端口号和IP地址,而 HTTP/2 多路复用是应用层协议在单个TCP连接内的优化。
- 通过流ID(类似套接字标识),避免了频繁建立TCP连接的开销。
QUIC 的多路复用
尽管 HTTP/2 提供了传输层多路复用,但数据仍然在单一 TCP 上进行传递。如果这个 TCP 某个包丢失而产生阻塞,HTTP/2 的多路复用中的其他流同样会被阻塞。
QUIC 由于构建在 UDP 之上,而 UDP 不会阻塞的问题。因此,QUIC 的流是彻底独立传输,一条“流”阻塞不会影响其他“流”继续传输。
TCP 传输示例:
流1: A — B — C — D
流2: E — F — G — H
如果 B 丢失,TCP 会暂停所有数据一直等重传成功:
... A — (丢失 B) — 等待 B — C — ... << 阻塞
QUIC 示例:
流1: A — B — C — D
流2: E — F — G — H
如果 B 丢失,只暂停流1的重传:
流1: A — (丢 B) — 重传 B — C — ...
流2: E — F — G — H (照常继续,无阻塞)
参考资料: