HTTP/3:QUIC 替代 TCP
2022 年,HTTP/3 正式成为 RFC,这意味着它已经从实验性协议变成了标准。
HTTP/3 的最大改变是:它不再基于 TCP,而是基于 QUIC(一个基于 UDP 的协议)。
这个改变解决了 HTTP/2 的一些根本性问题。
为什么需要 HTTP/3?
HTTP/2 的遗留问题
HTTP/2 虽然解决了 HTTP/1.1 的很多问题,但仍然有一个根本缺陷:TCP 的队头阻塞。
HTTP/2 多路复用:
Stream 1 ──────────────────────────> 完成
Stream 2 ─────────> 丢包 ──> 重传 ──> 完成
Stream 3 ──> 丢包 ──> 重传 ──────────────> 完成
TCP 层的问题:丢包会影响所有 Stream虽然每个 HTTP/2 Stream 是独立的,但它们共享同一个 TCP 连接。TCP 必须按顺序交付数据,一个包的丢失会导致所有后续包被卡住。
解决方案:QUIC
QUIC 是一个新的传输层协议,构建在 UDP 之上:
QUIC 的设计目标:
1. 解决 TCP 队头阻塞
2. 0-RTT 或 1-RTT 连接建立
3. 连接迁移
4. 可插拔的拥塞控制QUIC 核心机制
Stream 级别的可靠性和有序性
TCP 的可靠性:整个连接有序
QUIC 的可靠性:每个 Stream 有序
TCP:
包 1 ───> 包 2 ───> 包 3 ───> 包 4 ───>
↑
包 2 丢失
包 3 和包 4 都要等
QUIC(Stream 1):
帧 1 ───> 帧 2 ───> 帧 3 ───> 完成
↑
Stream 1 的帧 2 丢失
只影响 Stream 1
QUIC(Stream 2):
帧 A ───> 帧 B ───> 帧 C ───> 完成
(Stream 2 不受影响,继续传输)连接建立
TCP + TLS 1.3(理想情况):
1. TCP 三次握手(1 RTT)
2. TLS 握手(1 RTT)
总计:2 RTT
TCP + TLS 1.2:
1. TCP 三次握手(1 RTT)
2. TLS 握手(2 RTT)
总计:3 RTT
QUIC + TLS 1.3:
1. 握手和密钥交换同时进行(1 RTT)
总计:1 RTT
QUIC 0-RTT(重连时):
0 RTT(使用缓存的密钥)连接迁移
TCP 连接基于四元组(源 IP, 源端口, 目标 IP, 目标端口)
手机从 WiFi 切换到 4G:
- IP 地址变了
- TCP 连接断开
- 需要重新建立连接
QUIC 使用 Connection ID:
- 连接与 IP 地址无关
- 切换网络时,只要 Connection ID 不变,连接保持HTTP/3 帧类型
┌─────────────────────────────────────────────────────────────┐
│ HTTP/3 帧类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ SETTINGS 帧 ── 连接参数 │
│ HEADERS 帧 ── HTTP 头部 │
│ DATA 帧 ── HTTP 消息体 │
│ CANCEL_PUSH 帧 ── 取消推送 │
│ PING 帧 ── 心跳检测 │
│ GOAWAY 帧 ── 优雅关闭 │
│ MAX_PUSH_ID 帧 ── 最大推送 ID │
│ ... │
│ │
└─────────────────────────────────────────────────────────────┘与 HTTP/2 帧的对比
HTTP/2 帧 vs HTTP/3 帧:
HTTP/2:
├─ HEADERS(压缩的头部)
├─ DATA(数据)
├─ SETTINGS(参数)
├─ PUSH_PROMISE(服务器推送)
├─ PING
├─ WINDOW_UPDATE(流控制)
└─ GOAWAY
HTTP/3:
├─ HEADERS(未压缩,直接用 QPACK)
├─ DATA(数据)
├─ CANCEL_PUSH(取消推送,替代 PUSH_PROMISE)
├─ PING
└─ GOAWAY
注意:HTTP/3 没有 WINDOW_UPDATE!
QUIC 本身已经有流控制机制,不需要 HTTP 层再做QPACK:HTTP/3 的头部压缩
HPACK 的局限
HTTP/2 使用 HPACK 压缩头部,但 HPACK 依赖 TCP 的有序传输。
如果 TCP 包丢失,HPACK 的动态表更新会被卡住,影响后续帧的解析。
QPACK 的解决方案
QPACK 使用两个单向流(Unidirectional Streams):
1. 请求流:发送 HEADERS 帧
2. 动态表流:发送动态表更新
动态表更新和 HEADERS 可以独立传输,不互相阻塞。QPACK 编码示例
HEADERS 帧编码:
┌─────────────────────────────────────────────────────────────┐
│ QPACK 编码 │
├─────────────────────────────────────────────────────────────┤
│ Static Table Reference │ Dynamic Table Reference │
│ (预定义索引) │ (动态索引) │
│ │
│ 03 │ c0 │
│ (= :method GET) │ (= 动态表索引 0) │
└─────────────────────────────────────────────────────────────┘HTTP/3 的握手过程
1-RTT 握手
客户端 服务器
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ QUIC Initial (CRYPTO/CH) │ │
│ │ - DCID (目标连接 ID) │ │
│ │ - SCID (源连接 ID) │ │
│ │ - TLS ClientHello │ │
│ └─────────────────────────────────────────┘ │
│ ─────────────────────────────────────────────────> │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ QUIC Initial (CRYPTO/SH) │ │
│ │ - TLS ServerHello │ │
│ │ - TLS 证书 │ │
│ │ - TLS Finished │ │
│ └─────────────────────────────────────────┘ │
│ <──────────────────────────────────────────────── │
│ │
│ 握手完成,密钥已协商 │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ HTTP/3 HEADERS (GET /) │ │
│ │ + 加密 │ │
│ └─────────────────────────────────────────┘ │
│ ─────────────────────────────────────────────────> │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ HTTP/3 HEADERS (200 OK) │ │
│ │ + 加密 │ │
│ └─────────────────────────────────────────┘ │
│ <──────────────────────────────────────────────── │0-RTT 握手
前提:客户端和服务器之前建立过连接
客户端 服务器
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ QUIC Initial │ │
│ │ + Early Data (0-RTT) │ │
│ │ + 加密(使用上次保存的密钥) │ │
│ └─────────────────────────────────────────┘ │
│ ─────────────────────────────────────────────────> │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ QUIC Initial │ │
│ │ + 新密钥(可能拒绝 0-RTT 数据) │ │
│ └─────────────────────────────────────────┘ │
│ <──────────────────────────────────────────────── │HTTP/3 的优势
1. 更低的延迟
场景:首次访问网站
TCP + TLS 1.3 = 2 RTT
QUIC + TLS 1.3 = 1 RTT
节省 1 个 RTT ≈ 节省 20-100ms(取决于网络)2. 无 TCP 队头阻塞
在丢包率 2% 的网络下:
HTTP/2:有效吞吐量下降 50%+
HTTP/3:有效吞吐量下降 < 10%3. 更好的移动网络支持
场景:地铁上从 4G 切换到 5G
TCP:连接断开,重新握手
QUIC:Connection ID 不变,无缝切换4. 更好的拥塞控制
TCP 拥塞控制在内核中,更新困难
QUIC 拥塞控制在用户空间,更新像更新 App 一样简单
这意味着:
- 新的拥塞控制算法可以更快部署
- 可以针对不同应用使用不同算法
- 可以根据网络状况动态调整部署现状
浏览器支持
Chrome: 支持(2020 年默认启用)
Firefox: 支持
Safari: 支持(iOS 14+)
Edge: 支持服务器支持
Nginx: 1.25+(实验性支持)
Caddy: 默认支持
Apache: mod_h2(第三方模块)
Cloudflare: 支持
Fastly: 支持如何启用
nginx
# Nginx 配置
server {
listen 443 quic reuseport;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 声明 HTTP/3 可用
add_header Alt-Svc 'h3=":443"; ma=86400';
}bash
# curl 测试
curl -I --http3 https://example.com
# Chrome 开发者工具
# 查看协议:Protocol 列会显示 h3-29 或 http/3HTTP/3 vs HTTP/2 vs HTTP/1.1
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | QUIC/UDP |
| 多路复用 | 无 | Stream | Stream(更完善) |
| 头部压缩 | 无 | HPACK | QPACK |
| 服务器推送 | 无 | 支持 | 支持 |
| 队头阻塞 | TCP 层 | TCP 层 | Stream 级别 |
| 握手延迟 | 3 RTT | 2-3 RTT | 0-1 RTT |
| 连接迁移 | 无 | 无 | 支持 |
| 拥塞控制 | 内核 | 内核 | 用户空间 |
| TLS | 可选 | 可选 | 必需 |
面试追问方向
- HTTP/3 相比 HTTP/2 最大的改进是什么?
- 为什么 HTTP/3 基于 UDP 而不是 TCP?
- 什么是 QUIC?它的核心特性是什么?
- HTTP/3 如何解决 TCP 队头阻塞问题?
- 什么是 0-RTT 和 1-RTT?它们有什么区别?
- 什么是 QPACK?和 HPACK 有什么区别?
- HTTP/3 的连接迁移是如何工作的?
- HTTP/3 部署现状如何?
- 如何在 Nginx 中启用 HTTP/3?
- HTTP/3 的 0-RTT 有什么安全风险?
