QUIC 协议:HTTP/3 底层协议
2015 年,Google 提出了 QUIC 协议。
2022 年,HTTP/3 正式成为 RFC 9114,QUIC 成为 HTTP/3 的底层传输协议。
QUIC 是一个基于 UDP 的传输层协议,但它实现了 TCP 的可靠性,还解决了 TCP 的一些固有问题。
为什么要发明 QUIC?
TCP 已经用了 40 多年,为什么还需要新的协议?
TCP 的问题
1. 连接建立慢
- 三次握手 = 1.5 RTT
- TLS 握手 = 1-2 RTT
- 总计:2.5-3.5 RTT 才能开始传输数据
2. 队头阻塞
- TCP 保证有序,一个包丢了,后面的都要等
- HTTP/2 多路复用:一个流丢包,其他流也阻塞
3. 连接迁移困难
- 手机从 WiFi 切换到 4G,IP 变了,TCP 连接断了
- 需要重新建立连接
4. 拥塞控制僵化
- 拥塞控制算法嵌入内核,更新困难
- 无法在应用层定制QUIC 的解决思路
QUIC 的设计目标:
1. 0-RTT 或 1-RTT 建立连接
2. 无队头阻塞的多路复用
3. 连接迁移(Connection Migration)
4. 可插拔的拥塞控制
5. 前向纠错(FEC)
6. 连接 ID(Connection ID)QUIC 协议结构
QUIC 头部
┌─────────────────────────────────────────────────────────────┐
│ QUIC 头部(可变长度) │
├─────────────────────────────────────────────────────────────┤
│ 头部Protected │ 连接 ID长度 │ 连接 ID │
│ (1-4 字节) │ (1 字节) │ (0-20 字节) │
├─────────────────────────────────────────────────────────────┤
│ 版本号 / 包编号 / 加密信息 │
├─────────────────────────────────────────────────────────────┤
│ 帧(Frames) │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │HEADERS│ │DATA │ │ACK │ │ ... │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
└─────────────────────────────────────────────────────────────┘QUIC vs TCP + TLS 头部开销
TCP + TLS 1.3(最理想情况):
┌─────────┬─────────────┬─────────────┬────────────────────┐
│ TCP 头 │ TLS 头 │ 应用数据头 │ 实际数据 │
│ 20 字节 │ 加密开销 │ HTTP/2 │ │
└─────────┴─────────────┴─────────────┴────────────────────┘
QUIC(最理想情况):
┌─────────┬─────────────┬────────────────────┐
│ QUIC 头 │ 帧头(极小) │ 加密后的实际数据 │
│ 最小 4B │ │ │
└─────────┴─────────────┴────────────────────┘
QUIC 将 TLS 加密直接集成到 QUIC 层,减少了头部开销。QUIC 的核心特性
1. 0-RTT 和 1-RTT 连接建立
1-RTT(首次连接)
传统 TCP + TLS 1.3:
客户端 ──── SYN ────────────────────────────────> 服务端
客户端 <─── SYN+ACK ──────────────────────────── 服务端
客户端 ──── ClientHello (TLS) ──────────────────> 服务端
客户端 <─── ServerHello + 证书 + 加密算法 ──────── 服务端
客户端 ──── Finished (加密密钥) ────────────────> 服务端
(密钥已就绪,可以加密发送)
客户端 ──── 加密的应用数据 ───────────────────────> 服务端
总计:2 RTT(不含应用数据)到 3 RTT(含 TLS)QUIC 1-RTT:
客户端 ──── Initial (ClientHello) ────────────────> 服务端
客户端 <─── Initial (ServerHello + 证书) ────────── 服务端
客户端 <─── Handshake (加密参数) ────────────────── 服务端
客户端 ──── 0-RTT 数据(使用会话票据密钥) ────────> 服务端
(密钥已就绪,可以加密发送)
客户端 ──── 加密的应用数据 ───────────────────────> 服务端
总计:1 RTT0-RTT(重连)
QUIC 0-RTT:
首次连接时,服务端给客户端一个「会话票据」(Session Ticket)
客户端保存这个票据
重连时:
客户端 ──── Initial + 0-RTT 数据(使用会话密钥加密) ──> 服务端
客户端 <─── Initial (确认密钥) ────────────────────────── 服务端
客户端 <─── Handshake ──────────────────────────────────── 服务端
总计:0 RTT(无需等待就可以发送数据)
注意:0-RTT 数据可能受到重放攻击,因此仅用于幂等请求2. 无队头阻塞的多路复用
TCP 的队头阻塞
HTTP/2 多路复用(在 TCP 上):
Stream 1: ████████████
Stream 2: ██████
Stream 3: ██████████████████████
场景:Stream 2 丢了一个包
Stream 1: ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░
Stream 2: ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
Stream 3: ██████████████████████░░░░░░░░░░░░░░░░░░░░
↑
Stream 2 丢包
其他 Stream 都要等
TCP 层阻塞QUIC 的无阻塞
QUIC Stream(在 QUIC 上):
Stream 1: ████████████
Stream 2: ██████
Stream 3: ██████████████████████
场景:Stream 2 丢了一个包
Stream 1: ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
Stream 2: ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
Stream 3: ██████████████████████░░░░░░░░░░░░░░░░░░░░
↑
Stream 2 丢包
只影响 Stream 2
其他 Stream 不受影响QUIC 的每个 Stream 有独立的序号,丢包只影响本 Stream。
3. 连接迁移(Connection Migration)
TCP 的问题
手机从 WiFi 切换到 4G:
- IP 地址变了
- TCP 连接基于四元组(源IP, 源端口, 目标IP, 目标端口)
- IP 变了,连接断了
- 需要重新建立连接QUIC 的解决方案
QUIC 使用连接 ID(Connection ID):
- 每个连接有一个或多个连接 ID
- 连接 ID 与 IP 地址无关
- 切换网络时,只要连接 ID 不变,连接就保持
WiFi: Connection ID = abc123
│
│ ─── QUIC 包 (Connection ID = abc123) ────> 服务端
│ │
▼ ▼
4G: Connection ID = abc123 服务端
│ │
│ ─── QUIC 包 (Connection ID = abc123) ────> 服务端
│ │
│ 连接保持,数据继续传输 │4. 可插拔的拥塞控制
TCP 的拥塞控制:
- 算法嵌入内核
- 更新需要升级内核
- 周期:以年为单位
QUIC 的拥塞控制:
- 算法在用户空间实现
- 可以随时更新(像更新应用一样)
- 可以为不同连接使用不同算法
- 周期:以周为单位
可用的拥塞控制算法:
- CUBIC(默认)
- BBR
- COPPA
- 定制算法QUIC 与 HTTP/3
HTTP/3 的请求-响应模型
HTTP/3 over QUIC:
┌─────────────────────────────────────────────────────────────┐
│ HTTP/3 │
│ ├─ 请求/响应 │
│ ├─ 首部压缩(QPACK) │
│ └─ 服务器推送 │
├─────────────────────────────────────────────────────────────┤
│ QUIC │
│ ├─ 连接管理 │
│ ├─ 流控制 │
│ ├─ 拥塞控制 │
│ ├─ 加密 │
│ └─ 多路复用 │
├─────────────────────────────────────────────────────────────┤
│ UDP │
└─────────────────────────────────────────────────────────────┘HTTP/3 的帧类型
┌─────────────────────────────────────────────────────────────┐
│ HTTP/3 帧类型 │
├─────────────────────────────────────────────────────────────┤
│ HEADERS │ HTTP 首部 │
│ DATA │ HTTP 请求体/响应体 │
│ SETTINGS │ 协议参数 │
│ PING │ 连接保活 │
│ GOAWAY │ 优雅关闭 │
│ MAX_DATA │ 流控制 │
│ ... │ │
└─────────────────────────────────────────────────────────────┘QUIC 的部署现状
主流支持
浏览器支持:
- Chrome: 支持 HTTP/3(2020 年默认启用)
- Firefox: 支持 HTTP/3
- Safari: 支持 HTTP/3
服务器支持:
- Nginx: 1.25+(需要 OpenSSL 3.0+)
- Caddy: 默认支持 HTTP/3
- Apache: mod_http3(第三方模块)
CDN 支持:
- Cloudflare: 支持 HTTP/3(2021 年)
- Fastly: 支持 HTTP/3如何启用 HTTP/3
nginx
# Nginx 配置
http {
# 启用 HTTP/3
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_conf_command Options TLSv1.3;
server {
listen 443 ssl;
# 监听 QUIC(UDP 443)
listen 443 quic reuseport;
# 宣称为 HTTP/3 可用
add_header Alt-Svc 'h3=":443"; ma=86400';
}
}QUIC vs TCP + TLS 对比
| 特性 | TCP + TLS | QUIC |
|---|---|---|
| 连接建立 | 2-3 RTT | 0-1 RTT |
| 队头阻塞 | TCP 层阻塞 | Stream 级无阻塞 |
| 连接迁移 | 不支持 | 支持 |
| 拥塞控制 | 内核控制 | 用户空间可插拔 |
| 首部加密 | 仅负载加密 | 全连接加密 |
| 可靠性 | TCP 保证 | QUIC 保证 |
| 传输层 | TCP | UDP |
| 多路复用 | HTTP/2 层 | QUIC 层 |
面试追问方向
- QUIC 协议是什么?它解决了什么问题?
- QUIC 为什么基于 UDP 而不是 TCP?
- 什么是 0-RTT 和 1-RTT?它们有什么区别?
- QUIC 是如何实现无队头阻塞的?
- 什么是连接迁移?为什么 QUIC 支持连接迁移?
- HTTP/3 和 HTTP/2 有什么区别?
- QUIC 的拥塞控制为什么更容易更新?
- 0-RTT 有什么安全风险?
- QUIC 和 TCP 的头部开销有什么区别?
- 如何在生产环境中启用 HTTP/3?
