HTTP/2 协议基础:帧结构、流与多路复用、头部压缩、优先级与流控
在深入 Envoy 多租户故障域调优之前,必须透彻理解 HTTP/2 的核心机制。HTTP/2 并非简单的语义升级,而是从二进制帧、多路复用、头部压缩到流控的全面重构。这些机制直接决定了 Envoy 在代理层如何隔离租户流量、如何防止“慢请求”拖垮整个连接、以及如何通过流控参数实现精细化的资源配额管理。
2.1 HTTP/2 帧结构:二进制分帧层
HTTP/2 将 HTTP 语义(方法、路径、状态码等)封装在二进制帧中传输。帧是 HTTP/2 通信的最小单位,所有帧共享一个 9 字节的固定头部。
| 字段 | 长度 | 说明 |
|---|---|---|
| Length | 3 字节 | 帧负载的长度(不含帧头),最大 2^24 - 1 = 16,777,215 字节 |
| Type | 1 字节 | 帧类型:DATA、HEADERS、SETTINGS、PRIORITY、RST_STREAM、GOAWAY、WINDOW_UPDATE 等 |
| Flags | 1 字节 | 帧类型相关的标志位,如 END_STREAM (0x01)、END_HEADERS (0x04) |
| Stream Identifier | 4 字节 | 流 ID,奇数表示客户端发起的流,偶数表示服务端推送的流;0 为连接级帧 |
| Frame Payload | 可变 | 帧体,内容由 Type 决定 |
常见的帧类型及其在租户隔离场景中的作用:
- DATA 帧: 传输请求体或响应体。Envoy 通过 DATA 帧的流控窗口限制单个租户的吞吐量。
- HEADERS 帧: 传输 HTTP 头部。HPACK 压缩后,头部帧大小直接影响内存占用。
- SETTINGS 帧: 连接级参数协商,如初始窗口大小、最大并发流数。Envoy 可针对不同租户连接下发不同的 SETTINGS 值。
- RST_STREAM 帧: 立即终止某个流。用于快速隔离异常租户请求,防止其影响其他流。
- WINDOW_UPDATE 帧: 流量控制的核心,通知对端增加发送窗口。
- GOAWAY 帧: 优雅关闭连接,通知对端停止创建新流。Envoy 在连接池回收或故障转移时使用。
2.2 流与多路复用
流(Stream)是 HTTP/2 连接中独立的、双向的字节序列交换通道。每个流由唯一的 Stream ID 标识,承载一个完整的 HTTP 请求-响应交互。多路复用(Multiplexing)允许在同一个 TCP 连接上交错发送多个流的帧,彻底解决了 HTTP/1.x 的队头阻塞问题。
2.3 头部压缩 (HPACK)
HPACK 是 HTTP/2 专用的头部压缩算法,通过静态表、动态表和 Huffman 编码将头部体积压缩 85%-90%。这对于多租户场景至关重要——大量租户的请求头部可能包含长 、Cookie 或自定义元数据,HPACK 能显著降低内存和带宽开销。
| HPACK 组件 | 说明 | 租户隔离影响 |
|---|---|---|
| 静态表 | 预定义的 61 个常见头部字段(如 :method: GET、:path: /) | 所有租户共享,无隔离问题 |
| 动态表 | 连接中动态维护的头部索引表,大小由 SETTINGS_HEADER_TABLE_SIZE 控制(默认 4KB) | 关键隔离点: 动态表是连接级共享的。一个租户的头部可能污染另一个租户的索引,导致压缩效率下降或内存膨胀 |
| Huffman 编码 | 对头部字符串进行静态 Huffman 压缩 | 无状态,不影响隔离 |
envoy.http_connection_manager 的 http2_protocol_options 中的 hpack_table_size 设置为 0 或较小值(如 1KB),禁用或限制动态表,避免租户间头部索引污染。代价是压缩率下降,但隔离性提升。
2.4 优先级与流控
HTTP/2 提供了两种资源管理机制:优先级(Priority)和流量控制(Flow Control)。两者在 Envoy 多租户隔离中扮演不同角色。
2.4.1 优先级
HTTP/2 允许客户端通过 PRIORITY 帧或 HEADERS 帧中的优先级字段指定流的依赖关系和权重。优先级树决定了在连接资源紧张时,哪些流优先获得带宽。
- 依赖关系: 流可以依赖另一个流,形成树状结构。父流完成后,子流才能获得资源。
- 权重: 同一依赖层级下的流按权重比例分配带宽,权重值 1-256。
- 默认行为: 如果客户端不发送优先级信息,所有流默认权重为 16,无依赖。
weighted_clusters 或 priority 路由配置实现租户级优先级,而非依赖客户端优先级帧。
2.4.2 流量控制
HTTP/2 的流量控制是逐跳的、基于信用(Credit)的机制。每个流和整个连接都有独立的发送窗口。接收方通过 WINDOW_UPDATE 帧通知对端可以发送多少字节。
流控的核心参数及其在租户隔离中的调优方向:
| 参数 | 默认值 | 作用域 | 租户隔离调优建议 |
|---|---|---|---|
| SETTINGS_INITIAL_WINDOW_SIZE | 65535 字节 | 连接级 | 增大到 1MB-16MB 可提升吞吐,但会降低隔离精度。建议根据租户带宽配额动态调整 |
| SETTINGS_MAX_CONCURRENT_STREAMS | 无限制 (通常 100-1000) | 连接级 | 核心隔离参数: 限制每个连接的最大并发流数。Envoy 可针对不同租户连接设置不同值,防止单个租户耗尽流表 |
| SETTINGS_MAX_FRAME_SIZE | 16384 字节 | 连接级 | 保持默认或降低到 4096,防止大帧长时间占用连接 |
| WINDOW_UPDATE 增量 | 动态 | 流/连接级 | Envoy 可延迟发送 WINDOW_UPDATE 实现背压,控制租户的发送速率 |
http2_protocol_options 的 max_concurrent_streams 和独立的连接池来隔离这种影响。
2.5 关键帧交互流程:请求-响应生命周期
理解一个完整的 HTTP/2 请求-响应如何通过帧交互完成,是后续调优的基础。以下是一个典型流程:
- 连接建立: 客户端发送 Connection Preface(PRI * HTTP/2.0),随后双方交换 SETTINGS 帧协商参数。
- 发送请求: 客户端发送 HEADERS 帧(含 :method、:path 等伪头),Flags 包含 END_HEADERS。如果请求体存在,后续发送 DATA 帧,最后一个 DATA 帧带 END_STREAM 标志。
- 服务端处理: 服务端接收 HEADERS 和 DATA 帧,处理请求。
- 发送响应: 服务端发送 HEADERS 帧(含 :status),随后发送 DATA 帧,最后一个 DATA 帧带 END_STREAM 标志。
- 流关闭: 双方收到 END_STREAM 后,流进入半关闭或关闭状态。
- 流控交互: 在整个过程中,双方根据处理能力发送 WINDOW_UPDATE 帧调整窗口。
http2_protocol_options 中的 max_concurrent_streams 和 initial_stream_window_size 是租户隔离最直接的调优入口。