HTTP/3 是 HTTP 协议的最新主要版本,它最大的变革是放弃了基于 TCP 的传输,转而使用 Google 开发的 QUIC 协议(Quick UDP Internet Connections)。这一改变从根本上解决了 HTTP/2 中 TCP 队头阻塞和连接迁移的难题。
一、为什么需要 HTTP/3
1.1 HTTP/2 的 TCP 之痛
HTTP/2 的多路复用被 TCP 拖后腿:
TCP 连接
├── Stream 1 (数据包 A) ─────→ 丢失!
├── Stream 3 (数据包 B) ─────→ 到达
├── Stream 5 (数据包 C) ─────→ 到达
└── Stream 7 (数据包 D) ─────→ 到达
TCP 要求按序交付,即使 Stream 3/5/7 的数据已经到达,
也必须等待 Stream 1 的数据包 A 重传后才能继续交付
→ TCP 层的队头阻塞!
1.2 连接迁移困境
TCP 连接 = {源IP, 源Port, 目IP, 目Port}
手机 WiFi → 4G 切换:
源 IP 从 192.168.1.10 变为 10.0.0.5
→ TCP 四元组改变 → 连接中断
→ 需重新 DNS 解析、TCP 握手、TLS 握手、HTTP 请求
→ 延迟数秒
二、QUIC 协议核心
2.1 基于 UDP 的可靠传输
QUIC 层次结构:
┌──────────────────────────────────┐
│ HTTP/3(请求/响应语义) │
├──────────────────────────────────┤
│ QPACK(头部压缩) │
├──────────────────────────────────┤
│ QUIC Transport │
│ - 流多路复用 │
│ - 拥塞控制 │
│ - 可靠传输 │
├──────────────────────────────────┤
│ TLS 1.3(加密 + 认证 + 完整性) │
├──────────────────────────────────┤
│ UDP │
└──────────────────────────────────┘
QUIC 将 TCP + TLS + HTTP/2 的部分功能整合在 UDP 之上
2.2 内置 TLS 1.3
QUIC 握手 = 传输协商 + TLS 握手(1-RTT / 0-RTT)
标准 1-RTT 握手:
客户端 服务端
│ │
│ ── ClientHello ──────→ │
│ [包含 INITIAL 包] │
│ [传输参数] │
│ [TLS 密钥交换] │
│ │
│ ←─ ServerHello + ──────│
│ EncryptedExtensions │
│ Finished │
│ [包含 INITIAL + HANDSHAKE 包]
│ │
│ ── Finished ─────────→ │
│ │
│ ←── 应用数据 ──────────│ (只有 1 个 RTT!)
对比 TLS 1.2:
TCP 握手 (1 RTT) + TLS 握手 (2 RTT) + HTTP 请求 = 3 RTT
对比 TLS 1.3:
TCP 握手 (1 RTT) + TLS 握手 (1 RTT) = 2 RTT
QUIC + TLS 1.3:
同时完成传输和 TLS 握手 = 1 RTT (甚至 0-RTT)
2.3 0-RTT 重连
首次连接(1-RTT):
客户端 服务端
│ ── ClientHello ──────→ │
│ ←── ServerHello ───────│
│ ── Finished ─────────→ │
│ ←── 应用数据 ──────────│
│ │
(双方存储会话票据和新密钥)
后续连接(0-RTT):
客户端 服务端
│ ── ClientHello ──────→ │
│ [包含 0-RTT 数据] │
│ ←── ServerHello ───────│
│ [包含响应] │
客户端在发送第一个包时即携带应用数据!
但 0-RTT 数据存在重放攻击风险,通常只用于幂等请求
2.4 真正的流级多路复用
QUIC 的流独立性:
UDP 无连接
│
├── QUIC Stream 1 (Packet A) ─────→ 丢失!
│ - Stream 1 自己的重传逻辑
│ - 不影响其他流
│
├── QUIC Stream 3 (Packet B) ─────→ 到达 ✅
│ - 立即交付给应用层
│
├── QUIC Stream 5 (Packet C) ─────→ 到达 ✅
│ - 立即交付给应用层
│
└── QUIC Stream 7 (Packet D) ─────→ 到达 ✅
- 立即交付给应用层
Stream 1 的丢包只影响 Stream 1,其他流完全独立
→ 彻底消除队头阻塞
三、连接迁移
3.1 基于连接 ID
QUIC 连接不由四元组标识,而是由 64 位 Connection ID 标识
手机 WiFi → 4G 切换:
WiFi 连接:
{源IP: 192.168.1.10, 源Port: 54321}
→ QUIC Connection ID: 0x123456789ABCDEF0
4G 连接:
{源IP: 10.0.0.5, 源Port: 9876}
→ 相同的 QUIC Connection ID: 0x123456789ABCDEF0
服务端根据 Connection ID 识别是同一连接
→ 无需重新握手,直接继续通信
3.2 路径验证
客户端 服务端
│ (新路径) ── PATH_CHALLENGE ───→ │
│ [包含随机数据] │
│ │
│ ←── PATH_RESPONSE ─────────────│
│ [返回相同数据] │
│ [确认新路径可达] │
│ │
│ (旧路径发送 PATH_ABANDON) │
│ │
│ (新路径继续通信) │
四、QUIC 数据包结构
Long Header 包(用于握手):
1-Bit | 7-Bits | 1-2 Bits | 32 Bits | 0-160 Bits | 0-160 Bits
+------+--------+----------+----------+------------+------------+
|Flag=1| Version| DCIL/SCIL|Ver. Num | Dest ConnID| Src ConnID |
+------+--------+----------+----------+------------+------------+
| Token Length (i) | Token (..) |
+----------------------------+-----------------------------------+
| Length (i) | Packet Number (i) |
+----------------------------+-----------------------------------+
| Packet Payload (8 bytes, protected) |
+---------------------------------------------------------------+
Short Header 包(用于数据传输):
1-Bit | 7-Bits | 0-160 Bits | 0-32 Bits
+------+----------+-------------+-----------+
|Flag=0| Spin Bit | Dest ConnID | Pkt Num |
+------+----------+-------------+-----------+
| Packet Payload (protected) |
+--------------------------------------------+
五、HTTP/3 特性
5.1 QPACK 头部压缩
HTTP/2 的 HPACK 依赖有序流,不适用于 QUIC 的无序交付
→ HTTP/3 设计了 QPACK
QPACK = 动态表 + 静态表 + 编码器/解码器流
客户端编码请求头部:
- 使用动态表索引(通过 Encoder Stream 同步表更新)
- 无法解码时阻塞(Blocked)等待更新
服务端编码响应头部:
- 同样通过动态表索引
- 使用独立的 Encoder Stream 保证表一致性
对比 HPACK:
HPACK:表更新顺序依赖请求/响应顺序 → 队头阻塞
QPACK:表更新在独立流中传输 → 避免阻塞
5.2 流映射
HTTP/3 将请求映射到 QUIC 流:
QUIC Connection
│
├── Stream 0: QPACK Encoder Stream (双向)
│
├── Stream 1: QPACK Decoder Stream (双向)
│
├── Stream 2: 请求 1 (客户端发起,双向)
│ ├── Client → Server: HEADERS frame + DATA frame
│ └── Server → Client: HEADERS frame + DATA frame
│
├── Stream 6: 请求 2 (客户端发起,双向)
│
├── Stream 10: 请求 3 (客户端发起,双向)
│
... 每个 HTTP/3 请求使用独立的 QUIC 流
Stream ID 规则:
- 客户端发起:0, 4, 8, 12...(0 mod 4)
- 服务端发起:1, 5, 9, 13...(1 mod 4)
- 双向流:0/1 mod 4
- 单向流:2/3 mod 4
5.3 Java HTTP/3 客户端
// Java 21+ 使用 incubator HttpClient(需加 JVM 参数)
// --add-modules jdk.incubator.vector,jdk.incubator.foreign
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
public class Http3Client {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://cloudflare-quic.com"))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status: " + response.statusCode());
System.out.println("Version: " + response.version());
System.out.println("Body: " + response.body().substring(0, 200));
}
}
// 使用 Netty 构建 HTTP/3 服务器(需要 netty-incubator-codec-quic)
public class Http3Server {
public static void main(String[] args) throws Exception {
QuicSslContext sslContext = QuicSslContextBuilder.forServer(
new File("server.crt"),
new File("server.key")
)
.applicationProtocols("h3")
.build();
ChannelHandler codec = new QuicServerCodecBuilder()
.sslContext(sslContext)
.maxIdleTimeout(5000, TimeUnit.MILLISECONDS)
.initialMaxData(10000000)
.initialMaxStreamDataBidirectionalLocal(1000000)
.initialMaxStreamDataBidirectionalRemote(1000000)
.initialMaxStreamsBidirectional(100)
.tokenHandler(InsecureQuicTokenHandler.INSTANCE)
.handler(new ChannelInitializer<QuicChannel>() {
@Override
protected void initChannel(QuicChannel ch) {
ch.pipeline().addLast(new Http3ServerHandler());
}
})
.build();
NioEventLoopGroup group = new NioEventLoopGroup(1);
try {
Bootstrap bs = new Bootstrap();
Channel channel = bs.group(group)
.channel(NioDatagramChannel.class)
.handler(codec)
.bind(8443).sync().channel();
channel.closeFuture().await();
} finally {
group.shutdownGracefully();
}
}
}
六、HTTP 版本对比完整版
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | UDP + QUIC |
| 队头阻塞 | 严重 | 应用层消除 | 完全消除 |
| 握手延迟 | 2-3 RTT | 2-3 RTT | 0-1 RTT |
| 连接迁移 | 不支持 | 不支持 | 原生支持 |
| 拥塞控制 | TCP 控制 | TCP 控制 | 用户态可配置 |
| 头部压缩 | 无 | HPACK | QPACK |
| TLS | 可选 | 事实必需 | 内置 (1.3) |
七、部署现状
| 厂商/产品 | HTTP/3 支持 |
|---|---|
| Cloudflare | 默认启用 |
| 全面支持 | |
| 生产环境使用 | |
| Nginx | 实验模块 (nginx-quic) |
| Caddy | 默认启用 |
| Chrome | 默认启用 (2020+) |
| Firefox | 默认启用 (2021+) |
| Safari | 已支持 |
八、总结
| QUIC 优势 | 详细说明 |
|---|---|
| 低延迟 | 1-RTT / 0-RTT 握手 |
| 无队头阻塞 | 流级独立可靠性 |
| 连接迁移 | 网络切换不中断 |
| 安全性 | TLS 1.3 强制内置 |
| 可扩展 | 用户态实现,易于演进 |
HTTP/3 不是 HTTP 语法的改变,而是传输层的革命。QUIC 将 TCP 的可靠性、TLS 的安全性整合在 UDP 之上,解决了数十年来 HTTP 面临的核心性能瓶颈。随着主流浏览器和 CDN 的全面支持,HTTP/3 正在快速成为 Web 通信的新标准。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。