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 决定
Envoy 视角: 帧头中的 Stream Identifier 是租户隔离的关键。Envoy 可以通过流 ID 将不同租户的请求映射到不同的后端连接池或故障域,实现基于流的隔离粒度。

常见的帧类型及其在租户隔离场景中的作用:

  • DATA 帧: 传输请求体或响应体。Envoy 通过 DATA 帧的流控窗口限制单个租户的吞吐量。
  • HEADERS 帧: 传输 HTTP 头部。HPACK 压缩后,头部帧大小直接影响内存占用。
  • SETTINGS 帧: 连接级参数协商,如初始窗口大小、最大并发流数。Envoy 可针对不同租户连接下发不同的 SETTINGS 值。
  • RST_STREAM 帧: 立即终止某个流。用于快速隔离异常租户请求,防止其影响其他流。
  • WINDOW_UPDATE 帧: 流量控制的核心,通知对端增加发送窗口。
  • GOAWAY 帧: 优雅关闭连接,通知对端停止创建新流。Envoy 在连接池回收或故障转移时使用。
关键参数: 帧负载最大长度由 SETTINGS_MAX_FRAME_SIZE 控制,默认 16KB,最大 16MB。在租户隔离场景中,建议保持默认或适当降低,防止单个大帧阻塞连接。

2.2 流与多路复用

流(Stream)是 HTTP/2 连接中独立的、双向的字节序列交换通道。每个流由唯一的 Stream ID 标识,承载一个完整的 HTTP 请求-响应交互。多路复用(Multiplexing)允许在同一个 TCP 连接上交错发送多个流的帧,彻底解决了 HTTP/1.x 的队头阻塞问题。

HTTP/2 多路复用:单连接多流交错传输 客户端 服务端 单一 TCP 连接 流1 HEADERS 流1 DATA 流1 DATA 流2 HEADERS 流2 DATA 流3 HEADERS 流1 HEADERS 流1 DATA 流2 HEADERS 流2 DATA 流2 DATA 流3 HEADERS 时间 → 多路复用优势 • 流 1、流 2、流 3 的帧在连接上交错发送,互不阻塞 • 即使流 1 的 DATA 帧较大,流 2 的 HEADERS 帧仍可穿插发送 • Envoy 可基于流 ID 实现租户级优先级调度和流控
队头阻塞的残余: HTTP/2 解决了应用层的队头阻塞,但 TCP 层的队头阻塞依然存在。如果 TCP 包丢失,所有流都会等待重传。Envoy 通过多连接池和 QUIC 支持来缓解此问题。

2.3 头部压缩 (HPACK)

HPACK 是 HTTP/2 专用的头部压缩算法,通过静态表、动态表和 Huffman 编码将头部体积压缩 85%-90%。这对于多租户场景至关重要——大量租户的请求头部可能包含长 、Cookie 或自定义元数据,HPACK 能显著降低内存和带宽开销。

HPACK 组件 说明 租户隔离影响
静态表 预定义的 61 个常见头部字段(如 :method: GET、:path: /) 所有租户共享,无隔离问题
动态表 连接中动态维护的头部索引表,大小由 SETTINGS_HEADER_TABLE_SIZE 控制(默认 4KB) 关键隔离点: 动态表是连接级共享的。一个租户的头部可能污染另一个租户的索引,导致压缩效率下降或内存膨胀
Huffman 编码 对头部字符串进行静态 Huffman 压缩 无状态,不影响隔离
Envoy 调优建议: 对于多租户场景,建议将 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,无依赖。
Envoy 实践: Envoy 默认忽略客户端发送的优先级,而是使用自己的路由权重和连接池策略。在多租户场景中,建议在 Envoy 侧通过 weighted_clusters 或 priority 路由配置实现租户级优先级,而非依赖客户端优先级帧。

2.4.2 流量控制

HTTP/2 的流量控制是逐跳的、基于信用(Credit)的机制。每个流和整个连接都有独立的发送窗口。接收方通过 WINDOW_UPDATE 帧通知对端可以发送多少字节。

HTTP/2 流量控制:连接级与流级双窗口模型 发送方 (Envoy 上游) 接收方 (Envoy 下游) 连接级窗口 (Connection Window) 初始值: 65535 字节 (64KB) | 由 SETTINGS_INITIAL_WINDOW_SIZE 控制 流级窗口 (Stream Window) 每个流独立窗口 | 初始值: 65535 字节 (64KB) 流控规则 • 发送方只能发送 min(连接窗口剩余, 流窗口剩余) 字节 • 接收方处理完数据后,发送 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 实现背压,控制租户的发送速率
流控死锁风险: 如果接收方处理缓慢且不发送 WINDOW_UPDATE,发送方窗口耗尽后会完全停止发送。在多租户场景中,一个慢租户可能阻塞整个连接的流控更新。Envoy 通过 http2_protocol_options 的 max_concurrent_streams 和独立的连接池来隔离这种影响。

2.5 关键帧交互流程:请求-响应生命周期

理解一个完整的 HTTP/2 请求-响应如何通过帧交互完成,是后续调优的基础。以下是一个典型流程:

  1. 连接建立: 客户端发送 Connection Preface(PRI * HTTP/2.0),随后双方交换 SETTINGS 帧协商参数。
  2. 发送请求: 客户端发送 HEADERS 帧(含 :method、:path 等伪头),Flags 包含 END_HEADERS。如果请求体存在,后续发送 DATA 帧,最后一个 DATA 帧带 END_STREAM 标志。
  3. 服务端处理: 服务端接收 HEADERS 和 DATA 帧,处理请求。
  4. 发送响应: 服务端发送 HEADERS 帧(含 :status),随后发送 DATA 帧,最后一个 DATA 帧带 END_STREAM 标志。
  5. 流关闭: 双方收到 END_STREAM 后,流进入半关闭或关闭状态。
  6. 流控交互: 在整个过程中,双方根据处理能力发送 WINDOW_UPDATE 帧调整窗口。
Envoy 拦截点: Envoy 作为中间代理,会解析并可能修改这些帧。例如,Envoy 可以在 HEADERS 帧中插入租户标识,或根据租户配额提前发送 RST_STREAM 帧拒绝请求。Envoy 的 http2_protocol_options 中的 max_concurrent_streams 和 initial_stream_window_size 是租户隔离最直接的调优入口。