mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
3928 字
11 分钟
HTTP/3:QUIC 传输
2024-04-27

前面几篇实验,我们看到了 HTTP/2 如何用多路复用解决应用层的队头阻塞。但 HTTP/2 还有一个根本问题:它跑在 TCP 之上。TCP 本身也有队头阻塞,丢一个包,整条连接上的所有流都得等重传。HTTP/2 的多路复用只解决了应用层的排队问题,传输层的排队问题还在。

然而,HTTP/2 虽然在应用层实现了多路复用,但它仍然运行在 TCP 之上。TCP 作为传输层协议,有一个根本性的问题:队头阻塞(Head-of-Line Blocking)。当一个 TCP 数据包丢失时,即使后续数据包已经到达,TCP 也必须等待重传后才能继续交付数据。这对于现代网页(包含数十个资源请求)来说是灾难性的。

更糟糕的是,TCP 连接建立的延迟在现代 Web 中已难以忍受:

  • TCP 三次握手:1.5 RTT(客户端发送 SYN → 服务器回复 SYN-ACK → 客户端发送 ACK)
  • TLS 握手:1-2 RTT(取决于 TLS 版本和会话复用)
  • 总计:2.5-3.5 RTT 才能发送第一个 HTTP 请求

这意味着在高延迟网络(如移动网络)中,用户可能需要等待数百毫秒甚至数秒才能真正开始加载页面内容。

2012 年,Google 开始实验一个名为 SPDY 的协议,后来演变为 HTTP/2。同期,Google 也在研发一个全新的传输协议:QUIC(Quick UDP Internet Connections)。QUIC 的目标很明确:把 TCP 的可靠传输和 TLS 的安全性整合到 UDP 之上,彻底解决 TCP 的队头阻塞问题,同时大幅降低连接建立延迟。

2022 年,HTTP/3 作为 RFC 9114 正式发布,成为 HTTP 协议家族的最新成员。它最大的变化是:不再使用 TCP,而是运行在 QUIC 协议之上

一、QUIC 协议详解#

1.1 QUIC vs TCP:架构对比#

block-beta columns 1 block:traditional["传统 HTTP/1.1/2 架构"] HTTP["HTTP 层(请求/响应)"] TLS["TLS 层(加密)"] TCP["TCP 层(可靠传输、拥塞控制、流量控制)"] IP["IP 层(网络路由)"] end
block-beta columns 1 block:http3["HTTP/3 架构"] H3["HTTP/3 层(请求/响应,类似 HTTP/2 帧)"] QUIC["QUIC 层(可靠传输 + 内置 TLS 1.3 + 多路复用 + 拥塞控制)"] UDP["UDP 层(无连接传输)"] IP2["IP 层(网络路由)"] end

关键区别:

QUIC 内置加密:TLS 1.3 直接集成到 QUIC 握手中,所有 QUIC 数据包(包括握手包)都是加密的。这解决了 TCP + TLS 的「明文握手」问题。

QUIC 在 UDP 之上实现可靠性:QUIC 自己实现了丢包重传、拥塞控制、流量控制等机制。这意味着 QUIC 可以在不修改操作系统内核的情况下快速迭代。

QUIC 原生支持多路复用:QUIC 的「流」(Stream)概念直接支持多个独立的数据流,丢失一个流的数据包不会影响其他流。

1.2 连接建立:QUIC 如何实现 0-RTT#

TCP + TLS 1.2 的连接建立需要 2-3 个 RTT。QUIC 通过内置 TLS 1.3,可以实现惊人的 0-RTT 连接恢复

sequenceDiagram participant C as Client participant S as Server Note over C,S: 首次连接(1-RTT) C->>S: Initial (ClientHello) S->>C: Handshake (ServerHello, Cert, ...) C->>S: Handshake (Finished) Note over C,S: 连接建立完成,服务器可以立即发送数据 Note over C,S: 后续连接(0-RTT) C->>S: 0-RTT 数据包 + ClientHello Note over C: 客户端立即发送 HTTP 请求! S->>C: 服务器处理请求并发送响应

0-RTT 的原理是:客户端在首次连接成功后,服务器会返回一个「会话票据」(Session Ticket)。客户端保存这个票据,下次连接时可以立即发送加密数据,无需等待握手完成。

Note

0-RTT 数据有「重放攻击」风险,不适合非幂等请求(如支付、状态变更)。QUIC 协议本身不防止 0-RTT 重放,应用层需要自行处理。

1.3 解决队头阻塞:QUIC 的多流机制#

TCP 的队头阻塞问题可以用下图说明:

TCP 层面的队头阻塞

gantt title TCP:所有流共享一条连接,一个丢包阻塞全部 dateFormat X axisFormat %s section Stream 1 数据包1 :done, s1a, 0, 1 数据包2 :done, s1b, 1, 2 数据包3 丢失! :crit, s1c, 2, 3 等待重传... :active, s1d, 3, 5 数据包4 :done, s1e, 5, 6 数据包5 :done, s1f, 6, 7 section Stream 2 数据包A :done, s2a, 0, 1 数据包B(到达但被缓存,无法交付) :crit, s2b, 1, 7 section Stream 3 数据包X :done, s3a, 0, 1 数据包Y(同样被阻塞) :crit, s3b, 1, 7

QUIC 的独立流机制

gantt title QUIC:每个流独立,丢包只影响当前流 dateFormat X axisFormat %s section Stream 1 数据包1 :done, s1a, 0, 1 数据包2 :done, s1b, 1, 2 数据包3 丢失! :crit, s1c, 2, 3 等待重传... :active, s1d, 3, 5 数据包4 :done, s1e, 5, 6 数据包5 :done, s1f, 6, 7 section Stream 2 数据包A :done, s2a, 0, 1 数据包B :done, s2b, 1, 2 数据包C(正常交付) :done, s2c, 2, 3 section Stream 3 数据包X :done, s3a, 0, 1 数据包Y :done, s3b, 1, 2 数据包Z(正常交付) :done, s3c, 2, 3

QUIC 的每个流都有独立的序列号空间,丢失一个流的数据包不会影响其他流的数据交付。这对于现代网页加载多个资源(HTML、CSS、JS、图片等)来说意义重大。

1.4 连接迁移:网络切换不中断#

TCP 连接由四元组标识:(源 IP、源端口、目的 IP、目的端口)。当用户从 WiFi 切换到移动网络时,IP 地址改变,TCP 连接就会断开。

QUIC 使用 Connection ID 来标识连接,而不是依赖四元组:

TCP 连接切换

sequenceDiagram participant Phone as 手机 (WiFi: 192.168.1.100:54321) participant Server as 服务器 (203.0.113.1:443) Phone->>Server: TCP 连接建立 Phone-->>Server: 连接双向通信 Note over Phone: 用户切换到移动网络,IP 变为 10.0.0.50 Note over Phone,Server: 连接断开!需要重新建立连接

QUIC 连接迁移

sequenceDiagram participant Phone as 手机 (WiFi: 192.168.1.100:54321) participant Server as 服务器 (203.0.113.1:443) Phone->>Server: QUIC 连接 (Connection ID: 0xABC123) Phone-->>Server: 连接双向通信 Note over Phone: 用户切换到移动网络,IP 变为 10.0.0.50,端口变为 54322 Phone->>Server: 客户端发送带 Connection ID 的包 Server->>Phone: 服务器识别 Connection ID,继续连接 Note over Phone,Server: 连接保持,下载继续!

这对于移动用户来说是巨大的体验提升。当你在地铁里从 WiFi 切换到移动网络时,正在进行的下载、视频通话不会中断。

二、HTTP/3 的具体变化#

2.1 从 HTTP/2 到 HTTP/3:帧格式变化#

HTTP/3 仍然使用「帧」(Frame)的概念,但帧格式有所调整以适应 QUIC:

HTTP/2 帧格式(在 TCP 流上)

字段长度说明
Length24 bits帧负载长度
Type8 bits帧类型
Flags8 bits标志位
R1 bit保留位
Stream ID31 bits流标识符
Frame Payload可变帧负载

HTTP/3 帧格式(在 QUIC 流上)

字段长度说明
Typevarint帧类型
Lengthvarint帧负载长度
Frame Payload可变帧负载

主要变化:

帧头更简洁:HTTP/3 使用可变长度整数(varint),不再需要 24 位固定长度字段。移除了 Flags 和保留位,因为 QUIC 流本身已经处理了这些功能。

Stream ID 由 QUIC 管理:HTTP/2 的 Stream ID 在帧头中,HTTP/3 的流标识由 QUIC 层管理,不再出现在帧头中。

帧类型调整

HTTP/2 帧类型HTTP/3 对应说明
HEADERSHEADERS压缩的头部块
PRIORITY(移除)由 QUIC 流优先级替代
RST_STREAM(移除)由 QUIC 的 STOP_SENDING 替代
SETTINGSSETTINGS连接参数
PUSH_PROMISE(移除)HTTP/3 不支持服务器推送
PING(移除)由 QUIC 的 PING 替代
GOAWAYGOAWAY连接关闭通知
WINDOW_UPDATE(移除)由 QUIC 流量控制替代
CONTINUATION(移除)不再需要,QUIC 流独立

2.2 QPACK:HTTP/3 的头部压缩#

HTTP/2 使用 HPACK 进行头部压缩,依赖传输层的顺序性。由于 QUIC 的多流是独立的,HTTP/3 引入了 QPACK(QPACK Header Compression for HTTP):

HPACK (HTTP/2) 依赖动态表的顺序更新,如果 Stream 3 的 HEADERS 帧丢失,后续流就无法解码。

QPACK (HTTP/3) 的解决方案:

block-beta columns 1 block:qpack["QPACK 结构"] ES["**Encoder Stream(单向流)**\n- 发送动态表更新指令\n- 独立于请求流"] DS["**Decoder Stream(单向流)**\n- 确认已处理的动态表条目\n- 允许 Encoder 安全地重用条目"] RS["**Request/Response Streams**\n- 只包含压缩后的头部索引\n- 不受 Encoder/Decoder 流丢包影响"] end

QPACK 通过引入专门的编码器流和解码器流,实现了与 HPACK 相似的压缩效率,同时支持乱序处理。

2.3 流量控制与优先级#

HTTP/2 在应用层实现了流量控制和优先级,HTTP/3 则将这部分责任下放给 QUIC:

HTTP/2 的流量控制

  • 通过 WINDOW_UPDATE 帧管理
  • 连接级别和流级别两个层次
  • 应用层实现,与 TCP 流量控制可能冲突

HTTP/3 的流量控制

  • QUIC 层原生支持
  • 连接级别(所有流总和)和流级别
  • 自动流量控制帧(无需应用层处理)
  • 与 QUIC 的拥塞控制协同工作

优先级方面,HTTP/2 的优先级机制相当复杂,实际实现效果参差不齐。HTTP/3 简化了这一设计,鼓励使用更简单的优先级模型。

HTTP/2 在应用层实现流量控制,和 TCP 的流量控制可能冲突。一个常见问题:HTTP/2 的 WINDOW_UPDATE 帧和 TCP 的接收窗口独立运作,可能导致一方放行而另一方阻塞。QUIC 把流量控制收归传输层,和拥塞控制协同工作,避免了这种冲突。优先级方面,HTTP/2 的树形优先级模型太复杂,Chrome 实际简化为 5 级权重。HTTP/3 进一步简化,RFC 9218 定义了 Extensible Prioritization Scheme,鼓励使用更简单的优先级模型。

2.4 不支持服务器推送#

HTTP/2 引入了服务器推送,允许服务器在客户端请求 index.html 时主动推送 style.cssmain.js。初衷是减少往返:服务器知道 HTML 需要哪些资源,不用等客户端解析 HTML 后再来请求。但实际使用中问题不少。

推送命中率低。浏览器可能已经缓存了这些资源,服务器不知道,推了等于白推,浪费带宽。

推送缓存管理复杂。浏览器需要维护推送的缓存,和常规缓存协调。Chrome 的实现曾出现缓存一致性 bug,导致推送的资源被重复请求。

与预加载策略冲突。<link rel="preload">103 Early Hints 是客户端驱动的预加载,和服务器推送的服务端驱动逻辑冲突,浏览器不知道该听谁的。

HTTP/3 移除了服务器推送,取而代之的是 103 Early Hints 状态码。服务器在发送最终响应前,先发一个 103 响应告诉客户端”你接下来会需要这些资源”,客户端自己决定是否请求。从”推数据”变为”推提示”,客户端重新掌握了主动权。

三、实验一:观察 HTTP/3 连接#

QUIC 运行在 UDP 上,无法像前几篇那样用 nc 手动发送请求。但可以用 Caddy 搭建本地 HTTP/3 服务器,再用 curl 直连观察 QUIC 帧的交互。

3.1 搭建本地 HTTP/3 服务器#

Caddy 从 v2.6 起默认启用 HTTP/3,是最快的实验方式。

# 安装 Caddy(Ubuntu/Debian)
sudo apt install -y caddy
# 创建测试文件
mkdir -p /tmp/caddy-www
cat > /tmp/caddy-www/index.html << 'HTMLEOF'
<!DOCTYPE html>
<html>
<head><title>HTTP/3 Test</title></head>
<body>
<h1>HTTP/3 Test Server</h1>
<p>Hello from HTTP/3!</p>
</body>
</html>
HTMLEOF
cat > /tmp/caddy-www/data.json << 'JSONEOF'
{"protocol": "HTTP/3", "status": "ok", "server": "Caddy"}
JSONEOF
# 创建配置文件
cat > /tmp/Caddyfile << 'CADDYEOF'
localhost:8443 {
root * /tmp/caddy-www
file_server
tls internal
}
CADDYEOF
# 启动(前台运行,另开终端测试)
caddy run --config /tmp/Caddyfile

Caddy 会在 localhost:8443 上同时监听 TCP(HTTP/1.1、HTTP/2)和 UDP(HTTP/3)。

3.2 使用 curl 测试 HTTP/3#

大多数 Linux 发行版的默认 curl 不含 HTTP/3 支持。需要编译带 QUIC 的版本:

# 安装依赖
sudo apt install -y libngtcp2-dev libngtcp2-crypto-gnutls-dev libnghttp3-dev libgnutls28-dev
# 编译 curl
CURL_VER=8.13.0
curl -sL "https://curl.se/download/curl-${CURL_VER}.tar.gz" -o curl.tar.gz
tar xzf curl.tar.gz && cd curl-${CURL_VER}
./configure --with-gnutls --with-ngtcp2 --with-nghttp3
make -j$(nproc) && sudo make install

确认 HTTP/3 可用后,发起请求:

# 确认 HTTP/3 支持
/usr/local/bin/curl --version | grep HTTP3
# 发起 HTTP/3 请求
curl --http3 -k https://localhost:8443/

输出:

Terminal window
<!DOCTYPE html>
<html>
<head><title>HTTP/3 Test</title></head>
<body>
<h1>HTTP/3 Test Server</h1>
<p>Hello from HTTP/3!</p>
</body>
</html>

-v 观察连接详情:

curl --http3 -v -k https://localhost:8443/

输出:

Terminal window
* Connected to localhost (::1) port 8443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://localhost:8443/
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: localhost:8443]
* [HTTP/3] [0] [:path: /]
> GET / HTTP/3
> Host: localhost:8443
>
< HTTP/3 200
< content-type: text/html; charset=utf-8
< content-length: 166
< server: Caddy

关键标识:using HTTP/3GET / HTTP/3HTTP/3 200

3.3 比较三种协议的连接时间#

用 curl 的 -w 选项量化三种协议的差异:

# HTTP/1.1
curl --http1.1 -k -s -o /dev/null \
-w "Version: %{http_version}\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://localhost:8443/
# HTTP/2
curl --http2 -k -s -o /dev/null \
-w "Version: %{http_version}\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://localhost:8443/
# HTTP/3
curl --http3 -k -s -o /dev/null \
-w "Version: %{http_version}\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://localhost:8443/

本地 localhost 测试结果:

Terminal window
--- HTTP/1.1 ---
Version: 1.1
Connect: 0.000470s
TTFB: 0.002408s
Total: 0.002441s
--- HTTP/2 ---
Version: 2
Connect: 0.000293s
TTFB: 0.002360s
Total: 0.002419s
--- HTTP/3 ---
Version: 3
Connect: 0.002043s
TTFB: 0.003342s
Total: 0.003439s

本地测试中 HTTP/3 反而最慢,因为 QUIC 握手的加密计算开销比 TCP + TLS 更大。HTTP/3 的优势在高延迟网络(跨国访问、移动网络)和后续连接的 0-RTT 场景中才体现。

3.4 对比首次连接和二次连接#

0-RTT 的优势体现在二次连接上。先做一次请求让 curl 保存会话票据,再用保存的票据做第二次请求:

# 首次连接(1-RTT)
curl --http3 -k -s -o /dev/null \
-w "Version: %{http_version}\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://localhost:8443/
# 保存会话数据
curl --http3 -k -c /tmp/quic-cookies.txt https://localhost:8443/ > /dev/null
# 二次连接(可能 0-RTT)
curl --http3 -k -b /tmp/quic-cookies.txt -s -o /dev/null \
-w "Version: %{http_version}\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
https://localhost:8443/

注意观察二次连接的 TTFB 是否显著降低。如果本地测试差异不大(因为 localhost 延迟太低),可以想象在高延迟网络(100ms+ RTT)中,省掉一个 RTT 意味着首字节延迟减少 100ms 以上。

Warning

0-RTT 数据有重放攻击风险,不适合非幂等请求(如支付、状态变更)。QUIC 协议本身不防止 0-RTT 重放,应用层需要自行处理。对于 GET 请求这种幂等操作,0-RTT 是安全的。

四、实验二:用 Python 发起 HTTP/3 请求#

Python 标准库和主流 HTTP 库(如 httpx、requests)截至 2025 年尚不支持 HTTP/3。目前可用的是 aioquic,这是一个纯 Python 实现的 QUIC 和 HTTP/3 库。如果偏好命令行工具,建议使用上文的 curl 方案。

安装依赖:

pip install aioquic

HTTP/3 客户端示例(连接上文搭建的本地 Caddy 服务器):

#!/usr/bin/env python3
# http3_client.py -- HTTP/3 client demo using aioquic
import asyncio
from aioquic.asyncio.client import connect
from aioquic.h3.connection import H3Connection
from aioquic.h3.events import HeadersReceived, DataReceived
from aioquic.quic.configuration import QuicConfiguration
async def http3_get(url: str):
"""
发起 HTTP/3 GET 请求
需要: pip install aioquic
"""
from urllib.parse import urlparse
parsed = urlparse(url)
host = parsed.hostname
port = parsed.port or 443
path = parsed.path or "/"
# 创建 QUIC 配置
configuration = QuicConfiguration()
# 连接本地自签名服务器时需要跳过证书验证
configuration.verify_mode = 0
async with connect(host, port, configuration=configuration) as protocol:
# 创建 HTTP/3 连接
h3 = H3Connection(protocol._quic)
# 获取下一个可用的流 ID
stream_id = protocol._quic.get_next_available_stream_id()
# 发送请求头
h3.send_headers(
stream_id=stream_id,
headers=[
(b":method", b"GET"),
(b":scheme", b"https"),
(b":authority", f"{host}:{port}".encode()),
(b":path", path.encode()),
(b"user-agent", b"aioquic-h3-demo/1.0"),
],
)
# 发送空请求体,结束流
h3.send_data(stream_id=stream_id, data=b"", end_stream=True)
protocol.transmit()
# 接收响应
response_headers = {}
response_body = b""
# 通过 QUIC 事件驱动接收 H3 事件
while True:
event = protocol._quic.next_event()
if event is None:
await asyncio.sleep(0.01)
continue
for h3_event in h3.handle_event(event):
if isinstance(h3_event, HeadersReceived):
for name, value in h3_event.headers:
response_headers[name.decode()] = value.decode()
elif isinstance(h3_event, DataReceived):
response_body += h3_event.data
if h3_event.stream_ended:
return response_headers, response_body
if __name__ == "__main__":
headers, body = asyncio.run(http3_get("https://localhost:8443/"))
print(f"Status: {headers.get(':status', 'unknown')}")
print(f"Content-Type: {headers.get('content-type', 'unknown')}")
print(f"Body length: {len(body)} bytes")
print(f"Body: {body.decode()[:100]}...")

五、实验三:观察 QUIC 数据包#

使用 tcpdump 捕获本地 Caddy 的 QUIC 流量,可以直观看到 QUIC 运行在 UDP 之上:

# 在一个终端捕获 QUIC 流量(Caddy 监听在 8443 端口)
sudo tcpdump -i lo -n udp port 8443 -w quic_capture.pcap
# 在另一个终端发起 HTTP/3 请求
curl --http3 -k https://localhost:8443/
# 停止 tcpdump 后用 Wireshark 分析
wireshark quic_capture.pcap

在 Wireshark 中你会看到:

No. Time Source Dest Protocol Info
1 0.000 ::1 ::1 UDP 54321 8443 Len=1250 (QUIC Initial)
2 0.025 ::1 ::1 UDP 8443 54321 Len=1250 (QUIC Initial)
3 0.026 ::1 ::1 UDP 8443 54321 Len=1250 (QUIC Handshake)
4 0.051 ::1 ::1 UDP 54321 8443 Len=100 (QUIC Handshake)
5 0.075 ::1 ::1 UDP 8443 54321 Len=1250 (QUIC 1-RTT Protected)

关键观察:所有 QUIC 数据包都是 UDP 协议,不是 TCP;Initial 包虽然加密,但使用公开密钥,Wireshark 可以识别为 QUIC Initial;1-RTT Protected 包使用会话密钥加密,完全不可解密(除非有会话密钥);握手流程清晰可见:Initial → Handshake → 1-RTT Protected。

六、浏览器与服务器支持现状#

6.1 浏览器支持#

浏览器HTTP/3 支持默认启用说明
Firefox 88+稳定支持
Safari 16+macOS Ventura、iOS 16+
Edge 87+基于 Chromium
Opera 73+基于 Chromium

浏览器发现 HTTP/3 支持的方式:

  1. Alt-Svc 头:服务器返回 Alt-Svc: h3=":443"; ma=2592000,告诉客户端支持 HTTP/3
  2. HTTP/3 专用端口:客户端下次连接时尝试 HTTP/3

6.2 服务器支持#

主流 Web 服务器和 CDN 的支持情况:

服务器HTTP/3 支持说明
Nginx1.25+ 主线版本支持,需编译时启用 --with-http_v3_module
Apache2.4.58+ 可通过 mod_http3 实验性模块支持,需单独编译安装
Caddyv2.6+ 默认启用 HTTP/3
Traefikv2.5+ 支持 HTTP/3
Cloudflare全面支持 HTTP/3
AWS ALB支持 HTTP/3
GCP LB支持 HTTP/3
Fastly支持 HTTP/3

Nginx 配置示例:

# Nginx 1.25.1+ 配置 HTTP/3
server {
listen 443 quic reuseport; # QUIC 监听(启用 HTTP/3)
listen 443 ssl; # TCP 监听(HTTP/1.1/2 回退)
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Nginx 1.25.1+ 可用 http3 指令显式启用
http3 on;
# Alt-Svc 头,告诉浏览器支持 HTTP/3
add_header Alt-Svc 'h3=":443"; ma=86400';
location / {
root /var/www/html;
}
}

Caddy 配置(更简单):

# Caddy 默认启用 HTTP/3,无需额外配置
example.com {
root * /var/www/html
file_server
}

七、HTTP 版本对比总览#

特性HTTP/1.0HTTP/1.1HTTP/2HTTP/3
传输层协议TCPTCPTCPQUIC (UDP)
连接复用每次新建Keep-Alive多路复用多路复用
队头阻塞严重(连接级)严重(连接级)存在(TCP 级)已解决
头部压缩HPACKQPACK
加密可选可选可选强制(TLS 1.3)
连接建立延迟1-3 RTT1-3 RTT1-3 RTT0-1 RTT
服务器推送不支持不支持支持移除
连接迁移不支持不支持不支持支持
请求方法GET/POST/HEAD全部方法全部方法全部方法
二进制协议文本文本二进制帧二进制帧
发布年份19961997/199920152022
RFC 编号RFC 1945RFC 2616/7230RFC 7540/9113RFC 9114

八、何时选择 HTTP/3#

HTTP/3 并非万能药。在移动网络环境(WiFi 与蜂窝网络频繁切换)、高延迟网络(跨国访问、卫星网络)、大量并发请求的页面(SPA、资源密集型网站)和实时通信应用(视频会议、在线游戏)中,HTTP/3 的连接迁移和多路复用优势明显。

在网络稳定、延迟低的场景下,HTTP/2 仍然够用。如果服务器不支持 QUIC 或客户端环境受限,强行启用 HTTP/3 反而增加复杂度。

UDP 在某些网络环境下可能被限速或屏蔽,比如部分企业防火墙只放行 TCP 80/443。QUIC 的加密也有额外的 CPU 开销,首次连接的加密计算比 TCP + TLS 更重。另外,0-RTT 的重放风险需要应用层防范。这些因素都需要在选型时权衡。

九、观察总结#

HTTP/3 从 TCP 迁移到 QUIC,是 HTTP 协议在传输层的一次根本性架构变更。QUIC 把 TCP 的可靠传输和 TLS 的安全性整合到 UDP 之上,用 Connection ID 解决连接迁移,用独立流解决队头阻塞,用 0-RTT 缩短重连延迟。代价是首次连接的加密计算开销更大,0-RTT 的重放风险需要应用层防范,UDP 在部分网络环境可能受限。

帧格式从 HTTP/2 的 9 字节帧头简化为 varint 编码,QPACK 替代 HPACK 适配 QUIC 的乱序特性,服务器推送被移除。这些变化都指向同一个设计意图:让 HTTP 的工作方式匹配 QUIC 传输层的特性,而不是在 QUIC 上硬套 HTTP/2 的设计。


参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

HTTP/3:QUIC 传输
https://blog.souloss.cn/posts/web/http/http-3/
作者
Souloss
发布于
2024-04-27
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时