Files

215 lines
7.4 KiB
Markdown
Raw Permalink Normal View History

2026-05-24 11:42:38 +08:00
---
tags: [计算机网络, HTTP/2, HPACK, Multiplexing, Server Push]
create time: 2026-05-18 02:40
---
# HTTP/2 多路复用
## 概述
HTTP/2 (RFC 7540) 是 HTTP 协议自 1999 年 HTTP/1.1 以来的第一次重大修订。核心改进:**二进制分帧层**,让一个 TCP 连接上能并发多个请求,彻底消除 Head-of-Line blocking(应用层层面)。
```mermaid
flowchart TD
A["HTTP/1.1<br/>问题重重"] -->|"串行请求"| B["每个请求等响应"]
B --> C["慢 ❌"]
D["HTTP/2<br/>二进制分帧"] -->|"多路复用"| E["并发请求/响应"]
E --> F["快 ✅"]
style A fill:#FF6B6B,color:#fff
style D fill:#98FB98,color:#000
```
## HTTP/2 的三层层级
```
┌─────────────────────────────────────┐
│ Application Layer │
│ HTTP Frames / Streams │ ← 你操作的对象
├─────────────────────────────────────┤
│ Frame Layer │ ← HPACK + Flow Control
│ Binary Framing + Headers │
├─────────────────────────────────────┤
│ Transport Layer │ ← TCP (or QUIC for H3)
│ Connection Management │
└─────────────────────────────────────┘
```
## 二进制帧类型(Frame Types)
| Frame | 说明 |
|-------|------|
| **HEADERS** | 请求/响应头部 |
| **DATA** | 实际数据载荷 |
| RST_STREAM | 异常关闭流 |
| SETTINGS | 通信参数协商 |
| PUSH_PROMISE | 服务器推送 |
| PING | 延迟测量 |
| GOAWAY | 优雅关闭连接 |
| WINDOW_UPDATE | 流量控制 |
| CONTINUATION | HEADERS 太长时的延续帧 |
### HEADERS 帧结构
```
+---------------+
|Pad Length? |(0-7 bits)
|Padding (8-bit)|
|E |
|+---------------+----------------------------------------------+
| |Header Block Fragment (*) |
|+-----------------------------------------------------+
|... |
+-----------------------------------------------------+
```
## Stream — 多路复用的核心
HTTP/2 将每个请求/响应绑定到一个唯一的 **Stream ID**:
```
TCP Connection: client:45678 ↔ server:443
Stream 1: GET /index.html → 200 OK
Stream 3: GET /style.css → 200 OK
Stream 5: POST /api/login → 200 OK
Stream 7: GET /app.js → 200 OK
Client Server
│ │
│ ───HEADERS(stream=1)──→ │ GET /index.html
│ ───DATA(stream=1, 5KB)──→ │
│ │
│ ←──HEADERS(stream=1, 200 OK)── │
│ ←──DATA(stream=1, 15KB)── │
│ │
│ ───HEADERS(stream=3)──→ │ GET /style.css
│ ←──HEADERS(stream=3, 200 OK)── │
│ ←──DATA(stream=3, 3KB)── │
│ │
│ ───HEADERS(stream=5)──→ │ POST /api/login
│ ───DATA(stream=5, 50B)──→ │ body: {"user":"alice"}
│ ←──HEADERS(stream=5, 200 OK)── │
│ ←──DATA(stream=5, 80B)── │ body: {"token":"abc"}
```
### Stream 状态机
```
idle → reserved(local) ———→ half closed(local) —→ closed
↑ ↓ ↑
│ half closed(remote) │
│ ↓ │
└──── open ←───────── half closed(remote) ◄─────┘
↑ ↑
│ half closed(local)
│ ↓
send HEADERS closed
recv HEADERS
```
### 优先级分组(Stream Dependency)
```
Stream 1 (weight=16) [root]
├── Stream 3 (weight=8): /index.html
│ ├── Stream 7 (weight=4): style.css
│ └── Stream 9 (weight=2): logo.png
├── Stream 5 (weight=8): API data
└── Stream 11 (weight=1): tracking pixel
```
## HPACK — Header 压缩
HTTP/1.1 每次请求都重复发送 `User-Agent`、`Cookie`、`Accept` 等头部,浪费带宽。HPACK 用两种技术解决:
| 技术 | 说明 |
|------|------|
| **Header Table** (动态表) | 服务端缓存常用头,后续请求用索引代替 |
| **Huffman Coding** | 对字符串值做霍夫曼编码压缩 |
### HPACK 编码示例
原始 HTTP/1.1 请求头部(重复发送):
```
GET /page HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
Accept: text/html
Cookie: session=abc123
Accept-Encoding: gzip
```
重复 10 次后,HTTP/2 只发一次完整头部,后续使用动态表索引:
```
索引 1: Host: example.com (查表! 不重传)
索引 4: User-Agent: ... (查表!)
索引 5: Accept: text/html (查表!)
索引 6: Cookie: session=abc123 (查表!)
索引 7: Accept-Encoding: gzip (查表!)
```
**节省效果:** header 体积减少 70%~90%。
## Server Push
服务器可以主动向客户端推送资源,无需客户端请求:
```
Client: GET /index.html
Server: HEADERS(stream=2, 200 OK) — HTML content
Server: PUSH_PROMISE(stream=2, promised_stream=4)
→ style.css
Server: HEADERS(stream=4, 200 OK)
Server: DATA(stream=4, CSS content)
← 客户端在还没解析到 <link> 之前就已经收到了 CSS!
```
```bash
# Go 中启用 HTTP/2 Server Push
pusher, ok := w.(http.Pusher)
if ok {
pusher.Push("/static/style.css", nil)
}
```
> [!warning] Push 的实际争议
> Push 被广泛认为弊大于利:浏览器可能已经缓存了资源(导致冗余传输),且无法像 Client-initiated 那样利用浏览器已有的缓存决策。现代实践中更多使用 Preload `<link rel="preload">` 替代 Push。
## HTTP/2 vs HTTP/1.1 对比
| 特性 | HTTP/1.1 | HTTP/2 |
|------|---------|--------|
| 格式 | 文本 | **二进制** |
| 并发 | 需要 N 个连接 | **单连接多路复用** |
| Header | 明文全量发送 | **HPACK 压缩** |
| 服务端推送 | ❌ | ✅ (可选) |
| 头部压缩 | ❌ (需 HPACK extension) | ✅ 原生支持 |
| 头部顺序 | 按发送顺序 | 可重新排序 |
| HoL Blocking | 严重 (TCP 层) | **消除** (Stream 独立) |
| TLS 要求 | 不需要 | **推荐** (nginx/apache 默认 h2c) |
## h2c — HTTP/2 Clear Text (未加密)
HTTP/2 可以通过 ALPN 升级(TLS 下的 h2)或 HTTP Upgrade 机制(纯 TCP 的 h2c):
```bash
# curl 测试 h2c
curl --http2 -H "Upgrade-H2C: true" http://localhost:8080/api
# Go net/http 中 h2c 支持需 import golang.org/x/net/http2
import "golang.org/x/net/http2"
import "golang.org/x/net/http2/h2c"
```
> [!tip] 生产环境始终用 HTTPS + h2
> 浏览器仅支持 TLS 下的 HTTP/2 (ALPN = "h2")。纯 TCP 的 h2c 主要用于微服务间通信。
## 关联笔记
- [[hhs/NETWORK/HTTP/1.1完全指南]] — HTTP/2 的基础
- [[hhs/NETWORK/HTTP/3与QUIC]] — HTTP/2 的 UDP 替代品
- [[hhs/NETWORK/HTTPS与TLS握手]] — HTTP/2 通常配合 HTTPS