Skip to content

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 RTT

0-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 + TLSQUIC
连接建立2-3 RTT0-1 RTT
队头阻塞TCP 层阻塞Stream 级无阻塞
连接迁移不支持支持
拥塞控制内核控制用户空间可插拔
首部加密仅负载加密全连接加密
可靠性TCP 保证QUIC 保证
传输层TCPUDP
多路复用HTTP/2 层QUIC 层

面试追问方向

  • QUIC 协议是什么?它解决了什么问题?
  • QUIC 为什么基于 UDP 而不是 TCP?
  • 什么是 0-RTT 和 1-RTT?它们有什么区别?
  • QUIC 是如何实现无队头阻塞的?
  • 什么是连接迁移?为什么 QUIC 支持连接迁移?
  • HTTP/3 和 HTTP/2 有什么区别?
  • QUIC 的拥塞控制为什么更容易更新?
  • 0-RTT 有什么安全风险?
  • QUIC 和 TCP 的头部开销有什么区别?
  • 如何在生产环境中启用 HTTP/3?

基于 VitePress 构建