本页目录
HTTP/2
HTTP/2 用一条 TCP 连接承载多个并发 stream——帧复用、HPACK 头部压缩、服务器推送。队头阻塞从 HTTP 层消掉了,但转移到了 TCP 层(丢包阻塞所有 stream),这正是 HTTP/3 转向 QUIC 的原因。
概述
HTTP/2(RFC 7540, 2015)不改 HTTP 语义(methods/status codes/headers),但完全重写了传输层:文本 → 二进制分帧、一个 TCP 连接 → 多路复用 stream、header 冗余 → HPACK 压缩。目标是解决 HTTP/1.1 的性能瓶颈——队头阻塞和 header 暴增。尽管实践效果显著,TCP 层仍存在队头阻塞(丢包阻塞所有 stream),HTTP/3 继而转向 QUIC 解决此问题。Server Push 因使用率低已被 Chrome 移除。
HTTP/2 的目标
解决 HTTP/1.1 的性能瓶颈:
- Head-of-Line Blocking: 一个 request 慢 → 同连接所有 requests 等
- Header 冗余: 每个 request 重复大段 headers (Cookie, User-Agent, Accept)
- 连接数: browser 开 6-8 个 TCP 连接做并行 (resource waste + non-deterministic ordering)
HTTP/2: 多路复用 + header 压缩 + server push → 一个 TCP 连接高效处理所有请求。
Binary Framing
HTTP/2 是二进制协议 (不是文本):
Frame format (9 bytes header + variable payload):
Length (3 bytes): payload 长度 ≤ 2^14 (16384, default) or 2^24-1 (max frame size)
Type (1 byte): DATA(0x0), HEADERS(0x1), PRIORITY(0x2), RST_STREAM(0x3),
SETTINGS(0x4), PUSH_PROMISE(0x5), PING(0x6), GOAWAY(0x7),
WINDOW_UPDATE(0x8), CONTINUATION(0x9)
Flags (1 byte): depends on type
Reserved (1 bit) + Stream Identifier (31 bits): 哪个 stream
DATA frame:
flags: END_STREAM(0x1), PADDED(0x8)
HEADERS frame:
payload: HPACK-compressed header block fragment
flags: END_STREAM(0x1), END_HEADERS(0x4), PADDED(0x8), PRIORITY(0x20)
SETTINGS frame (connection-level):
只在 stream 0 发送:
SETTINGS_HEADER_TABLE_SIZE (0x1): HPACK 动态表最大大小 (default 4096)
SETTINGS_ENABLE_PUSH (0x2): 0=disable server push (most servers do)
SETTINGS_MAX_CONCURRENT_STREAMS (0x3): 最大并发 stream 数 (recommend ≥100)
SETTINGS_INITIAL_WINDOW_SIZE (0x4): 初始 flow control window (65535)
SETTINGS_MAX_FRAME_SIZE (0x5): 最大 frame size (16384-16777215)
SETTINGS_MAX_HEADER_LIST_SIZE (0x6): 最大 header列表大小 (bytes)
Stream Multiplexing
一个 TCP 连接 → 多个 stream (client-initiated: odd, server-initiated: even):
Stream 0: 控制帧 only (SETTINGS, GOAWAY)
Stream 1: GET /index.html
Stream 3: GET /style.css ← 并行! 不需要等 Stream 1 完成
Stream 5: GET /script.js ← 也并行
每个 stream:
- 独立的 flow control (WINDOW_UPDATE per stream)
- Stream 1 丢包 → TCP 重传 → stream 1 卡住
→ 对其他 streams 的影响: 理论上没有 (multiplexing at frame level)
→ 实际上: TCP 丢包阻塞所有 stream (这就是 HTTP/3 要解决的 TCP HOL blocking!)
HPACK
HPACK = 静态表 (61 entries) + 动态表 (per-connection) + Huffman encoding
静态表 (永不改变):
Index 1: :authority
Index 2: :method GET
Index 3: :method POST
Index 4: :path /
Index 5: :path /index.html
Index 6: :scheme http
Index 7: :scheme https
Index 8: :status 200
Index 9: :status 204
...
Index 61: www-authenticate
动态表 (FIFO, max size = SETTINGS_HEADER_TABLE_SIZE):
每个 HEADERS response 结束后 → 存入动态表 (if compressible)
→ 后续 HEADERS request → 只引用 index → 极俭省
与 gzip 压缩对比 (为什么不用 gzip):
- CRIME 攻击: gzip 压缩整个 header block → 压缩率泄露 cookie 内容
- HPACK: per-header compression → 无跨-header 压缩 → 安全
Server Push (已废弃)
HTTP/2 Server Push:
Client: GET /index.html
Server: 返回 index.html + PUSH_PROMISE (style.css) + push style.css data
→ 客户端预先收到 style.css → 不需要等 browser parse + GET
为什么废弃:
- 缓存问题: client 可能已经有 style.css (cache) → server push 浪费带宽
- 实际使用率低: Chrome 统计 <2% pushed assets were used
- 复杂性: server 不知道 client 的 cache state
Chrome 106+ 已移除 server push
参考
- RFC: 7540, 7541, 9113 (revision)
- 工具:
curl --http2 -v,nghttp2, Chromechrome://net-internals/#http2
Keywords: HTTP/2, multiplexing, HPACK, binary framing, SETTINGS, stream, flow control, server push