HTTP/2 多路复用原理
HTTP/2 如何通过二进制帧实现多路复用
问题
HTTP/2 中,多路复用的原理是什么?
解答
什么是多路复用
HTTP/1.1 的请求-响应模型是串行的:一个 HTTP 消息必须完成后,才能处理下一个。而 HTTP/2 允许多个消息在同一个连接上交织传输,这就是多路复用——在一个 TCP 连接上,多个 HTTP 消息同时工作。
HTTP/1.1 为什么不能多路复用
HTTP/1.1 是基于文本分割解析的协议。请求消息格式如下:
GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
服务端需要不断读入字节,直到遇到换行符(\n 或 \r\n)才能解析一行内容。这种解析方式存在两个问题:
一次只能处理一个消息:在遇到分隔符完成解析之前,无法停止或切换到其他消息。
内存分配不可预知:服务端不知道一行内容有多长,无法预先分配合适的缓冲区大小,既要保证解析效率,又要避免内存浪费。
HTTP/2 的帧结构
HTTP/2 是基于二进制帧的协议。帧是数据的基本单元,结构如下:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
前 9 个字节对每个帧都是固定的:
- Length (3 字节):帧负载的长度
- Type (1 字节):帧类型(HTTP/2 定义了 10 种帧类型)
- Flags (1 字节):帧标志位
- Stream Identifier (4 字节):流 ID,标识帧所属的流
服务端只需解析前 9 个字节,就能准确知道整个帧的大小和类型,实现了”一切可预知,一切可控”。
多路复用的实现
HTTP/2 引入了”流”的概念:流是连接上独立的、双向的帧序列交换。每个帧的 Stream ID 标识它属于哪个流。
多路复用的工作方式:
- 客户端发起多个请求,每个请求分配一个唯一的 Stream ID
- 请求被拆分成多个帧,每个帧携带 Stream ID
- 这些帧可以交错发送,不需要等待前一个请求完成
- 服务端根据 Stream ID 将帧重新组装成完整的请求
- 响应同样以帧的形式交错返回
例如,一个 POST 请求可能被拆分为:
- HEADERS 帧(Stream ID: 3)
- DATA 帧(Stream ID: 3)
同时另一个 GET 请求的帧也在传输:
- HEADERS 帧(Stream ID: 5)
这些帧在同一个 TCP 连接上交错传输,实现了真正的并发。
关键点
- HTTP/2 基于二进制帧,每个帧前 9 字节包含长度、类型和流 ID,使解析可预知
- HTTP/1.1 基于文本分割,必须串行解析,无法预知内存需求
- 流(Stream)是帧的逻辑分组,通过 Stream ID 标识不同的请求/响应
- 多个流的帧可以在同一连接上交错传输,实现真正的并发
- 这种设计消除了 HTTP/1.1 的队头阻塞问题,显著提升了传输效率
目录