网络笔记:HTTP/2:多路复用与队头阻塞
发布于:
本文深入解析 HTTP/2 的核心特性:多路复用、流控、头部压缩,以及传输层队头阻塞问题。
HTTP/2 解决的是 HTTP/1.1 的两个痛点:
- 一个连接上并发很多请求时,HTTP/1.1 很容易靠”开更多 TCP 连接”硬扛(成本高)
- 应用层更需要”多路复用 + 优先级 + 流控”来管理并发
但它也带来一个经常被误解的点:
HTTP/2 消除了 HTTP/1.1 的应用层队头阻塞,但在 TCP 上仍然可能遭遇传输层队头阻塞(丢包会影响同连接内所有 stream)。
1. 最小模型:stream / frame / connection
- connection:底层仍是一个 TCP 连接(通常再叠 TLS)
- stream:一个逻辑请求-响应通道(带 stream id),同连接可并发多个
- frame:HTTP/2 的最小传输单元,多个 stream 的 frame 交错发送(这就是多路复用)
1.1 HTTP/2 的层次结构
flowchart TD
A[Application] --> B[HTTP/2]
B --> C[Stream 1]
B --> D[Stream 2]
B --> E[Stream N]
C --> F[Frame]
D --> F
E --> F
F --> G[TCP Connection]
G --> H[TLS]
H --> I[Network]
1.2 Frame 类型
flowchart TD
A[HTTP/2 Frame] --> B[HEADERS]
A --> C[DATA]
A --> D[SETTINGS]
A --> E[WINDOW_UPDATE]
A --> F[PING]
A --> G[GOAWAY]
B --> H[请求/响应头]
C --> I[请求/响应体]
E --> J[流控]
2. 多路复用带来的真实收益
能直接得到:
- 更少连接数:降低握手成本、降低内核资源占用、减少拥塞控制之间的相互干扰
- 更好的并发管理:请求可以在应用层交错发送,不需要为并发硬开 N 条 TCP
2.1 HTTP/1.1 vs HTTP/2
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: HTTP/1.1: 需要多个连接
C->>S: Connection 1: Request 1
C->>S: Connection 2: Request 2
C->>S: Connection 3: Request 3
S-->>C: Response 1
S-->>C: Response 2
S-->>C: Response 3
Note over C,S: HTTP/2: 单连接多路复用
C->>S: Stream 1: Request 1
C->>S: Stream 2: Request 2
C->>S: Stream 3: Request 3
S-->>C: Stream 1: Response 1
S-->>C: Stream 2: Response 2
S-->>C: Stream 3: Response 3
2.2 性能对比
flowchart TD
A[HTTP/1.1] --> B[多个 TCP 连接]
B --> C[高连接成本]
B --> D[拥塞控制干扰]
E[HTTP/2] --> F[单 TCP 连接]
F --> G[低连接成本]
F --> H[统一拥塞控制]
C --> I[性能差]
G --> J[性能好]
3. 但为什么还会”卡”:TCP 层队头阻塞(HoL)
关键事实:HTTP/2 的多路复用发生在 TCP 之上。
当 TCP 丢包时:
- TCP 必须按序交付字节流
- 丢失的那段字节没重传回来前,后面的字节即使到了也不能交给上层
于是同连接里所有 stream 的数据都可能被一起拖住 —— 这就是”HTTP/2 还会卡”的根因之一。
3.1 TCP 层 HoL Blocking
sequenceDiagram
participant App as HTTP/2 App
participant TCP as TCP
participant Network as Network
App->>TCP: Stream 1 Frame 1
App->>TCP: Stream 2 Frame 1 (丢失)
App->>TCP: Stream 3 Frame 1
App->>TCP: Stream 1 Frame 2
TCP->>Network: Packet 1
TCP->>Network: Packet 2 (丢失)
TCP->>Network: Packet 3
TCP->>Network: Packet 4
Network->>TCP: Packet 1
Network->>TCP: Packet 3
Network->>TCP: Packet 4
Note over TCP: 等待 Packet 2 重传
TCP->>TCP: 重传 Packet 2
Network->>TCP: Packet 2
TCP->>App: 交付所有数据
Note over App: 所有 Stream 都被阻塞
3.2 HoL 的影响
flowchart TD
A[TCP 丢包] --> B[按序交付要求]
B --> C[后续数据等待]
C --> D[所有 Stream 阻塞]
D --> E[延迟增加]
E --> F[吞吐下降]
4. 流控(flow control):别把它当成”优化”,它更像”背压”
HTTP/2 有连接级与 stream 级窗口(概念上理解即可):
- 读端处理不过来 → 窗口不增长 → 写端不能继续发
- 这是在保护系统:避免一端无限堆积内存
但工程上流控也会带来两类现象:
- 吞吐被窗口限制:尤其是大对象/长响应
- 尾延迟被背压放大:慢消费者会把同连接的发送节奏拖慢
4.1 流控机制
sequenceDiagram
participant Sender as Sender
participant Receiver as Receiver
Sender->>Receiver: Data (窗口: 100)
Receiver->>Receiver: 处理数据
Receiver->>Sender: WINDOW_UPDATE (窗口: 50)
Note over Sender: 窗口不足,等待
Receiver->>Receiver: 继续处理
Receiver->>Sender: WINDOW_UPDATE (窗口: 100)
Sender->>Sender: 可以继续发送
4.2 流控的影响
flowchart TD
A[流控] --> B[连接级窗口]
A --> C[Stream 级窗口]
B --> D[限制总发送]
C --> E[限制单 Stream]
D --> F[背压]
E --> F
F --> G[吞吐受限]
F --> H[延迟放大]
5. HPACK(头部压缩):收益和坑都很现实
收益:
- 头部重复性高(cookie/metadata)时,压缩能节省带宽与 CPU(取决于实现)
坑:
- 动态表管理不当会造成 CPU 开销上升
- 某些场景头部很大,仍会对延迟造成影响(尤其是小包/高并发)
5.1 HPACK 压缩
flowchart TD
A[HTTP Header] --> B[HPACK]
B --> C[静态表查找]
B --> D[动态表查找]
B --> E[字面量编码]
C --> F[压缩后 Header]
D --> F
E --> F
F --> G[带宽节省]
G --> H[CPU 开销]
5.2 头部压缩效果
| 场景 | 压缩前 | 压缩后 | 节省 |
|---|---|---|---|
| 重复头部 | 500 bytes | 50 bytes | 90% |
| 新头部 | 200 bytes | 150 bytes | 25% |
6. 怎么排障:先判断”卡在 TCP 还是卡在应用”
推荐顺序:
- 先看丢包/重传:如果丢包显著,同连接内所有 stream 变慢是正常现象
- 再看连接内并发与队列:是否把太多请求压在少量连接上?
- 再看流控/背压:是否存在慢消费者或窗口增长异常?
- 最后看 CPU:HPACK、TLS、协议栈开销是否成为瓶颈?
6.1 排障流程
flowchart TD
A[HTTP/2 性能问题] --> B{丢包/重传?}
B -->|是| C[TCP 层 HoL]
B -->|否| D{连接内并发?}
D -->|过多| E[连接压力大]
D -->|正常| F{流控问题?}
F -->|是| G[窗口/背压]
F -->|否| H{CPU 问题?}
H -->|是| I[HPACK/TLS 开销]
H -->|否| J[其他问题]
C --> K[优化网络/减少丢包]
E --> L[增加连接/优化并发]
G --> M[优化流控/背压]
I --> N[优化加密/压缩]
7. HTTP/2 vs HTTP/3
7.1 HTTP/3 的改进
flowchart TD
A[HTTP/2] --> B[TCP 层 HoL]
B --> C[所有 Stream 阻塞]
D[HTTP/3] --> E[基于 QUIC]
E --> F[UDP + 多路复用]
F --> G[Stream 独立]
G --> H[无 HoL Blocking]
7.2 选择建议
- HTTP/2:适合丢包率低的网络
- HTTP/3:适合丢包率高或需要低延迟的场景
8. 实际案例
8.1 案例:TCP HoL 导致的性能问题
问题:HTTP/2 多路复用下,一个请求慢导致其他请求也慢
分析:
- 单连接多 stream
- TCP 丢包导致 HoL blocking
- 所有 stream 都被阻塞
优化:
- 使用 HTTP/3(基于 QUIC)
- 或增加连接数
- 或优化网络质量
结果:性能提升 40%
9. 小结
HTTP/2 的核心价值是”连接内多路复用 + 更精细的并发控制”。但它并不是”万物加速器”:在 TCP 丢包时仍可能出现连接级的 HoL。排障时先把问题拆成:传输层(丢包/重传) vs 应用层(背压/并发/CPU),效率会高很多。
核心要点:
- HTTP/2 通过多路复用减少连接数
- TCP 层 HoL blocking 仍会影响所有 stream
- 流控提供背压保护,但也可能限制性能
- HPACK 压缩节省带宽,但有 CPU 开销
排障流程:
- 检查丢包/重传(TCP 层 HoL)
- 检查连接内并发和队列
- 检查流控和背压
- 检查 CPU(HPACK/TLS)