3. HTTP/2 协议基础:二进制分帧、多路复用、流优先级、头部压缩 (HPACK)
HTTP/2 是 gRPC 的默认传输协议,其核心设计目标是通过解决 HTTP/1.x 的队头阻塞(Head-of-Line Blocking)和低效的头部传输问题,显著降低网络延迟。理解 HTTP/2 的四大核心机制——二进制分帧、多路复用、流优先级和头部压缩 (HPACK)——是诊断服务网格中 gRPC 延迟问题的前提。
3.1 二进制分帧层 (Binary Framing Layer)
HTTP/2 将 HTTP 语义(方法、路径、头部等)封装在二进制格式的帧(Frame)中,而非 HTTP/1.x 的文本格式。这是所有高级特性的基础。
- 帧 (Frame):HTTP/2 中最小的通信单元,包含一个固定 9 字节的头部(Length, Type, Flags, Stream Identifier)和可变长度的负载(Payload)。
- 消息 (Message):一个完整的 HTTP 请求或响应,由一组逻辑相关的帧组成(例如:HEADERS 帧 + DATA 帧)。
- 流 (Stream):一个虚拟的、双向的、可复用的连接通道,用于在单个 TCP 连接上承载多个消息。每个流由唯一的整数标识符(Stream ID)标记。
延迟诊断要点: 二进制分帧使得协议解析更高效,但帧的组装顺序和流的状态管理直接影响延迟。在服务网格中,Envoy 或 Linkerd 代理需要解析这些帧以进行流量管理,帧处理不当(如乱序重组)会引入额外的代理延迟。
3.2 多路复用 (Multiplexing)
HTTP/2 允许在单个 TCP 连接上同时打开多个流,每个流独立地发送请求和接收响应。帧在连接上交错传输,接收方根据 Stream ID 将帧重组为完整的消息。
| 特性 | HTTP/1.x (无多路复用) | HTTP/2 (多路复用) |
|---|---|---|
| 连接数 | 通常 6-8 个并行 TCP 连接 | 单个 TCP 连接 |
| 队头阻塞 | 存在(请求必须排队等待响应) | 基本消除(流级别独立) |
| 资源开销 | 高(多个连接、TLS 握手) | 低(连接复用) |
| 延迟影响 | 高延迟,尤其在高 RTT 场景 | 低延迟,流间无阻塞 |
注意: 虽然 HTTP/2 消除了应用层的队头阻塞,但 TCP 层的队头阻塞仍然存在。如果 TCP 包丢失,所有流都会被阻塞,直到丢包重传完成。这是 gRPC 在弱网环境下延迟抖动的主要原因之一。
3.3 流优先级 (Stream Priority)
HTTP/2 允许客户端为每个流分配优先级,以告知服务器哪些请求更重要。优先级通过依赖树 (Dependency Tree) 和权重 (Weight) 实现。
- 依赖 (Dependency):流可以依赖另一个流,表示前者应在后者之后处理。
- 权重 (Weight):在同一个依赖层级中,权重值(1-256)决定了资源分配的比例。
- 默认行为:如果未设置优先级,所有流默认依赖根流(Stream 0),权重相同。
延迟诊断要点: 在服务网格中,如果 sidecar 代理错误地处理或重置了流的优先级,可能导致关键请求(如健康检查、控制面信令)被非关键请求(如批量数据流)阻塞,从而引发延迟尖刺。Envoy 的 HTTP/2 编解码器配置(如
envoy.http_connection_manager)中的优先级策略需要仔细审查。
3.4 头部压缩 (HPACK)
HTTP/2 使用 HPACK 算法对头部进行压缩,大幅减少头部开销。HPACK 结合了静态表、动态表和Huffman 编码。
| HPACK 组件 | 说明 | 延迟影响 |
|---|---|---|
| 静态表 | 预定义的 61 个常见头部字段(如 :method: GET、:status: 200) |
零延迟,直接索引 |
| 动态表 | 动态维护的已发送头部缓存,后续请求可引用索引 | 首次发送有编码延迟,后续请求延迟极低 |
| Huffman 编码 | 对字符串值进行压缩,减少字节数 | 轻微 CPU 开销,但显著减少传输时间 |
HPACK 的延迟特征在 gRPC 场景中尤为关键。gRPC 的头部通常包含 :path(服务名/方法名)、content-type、te 等字段。在服务网格中,sidecar 代理(如 Envoy)会解析并可能修改这些头部(例如添加 x-request-id、authority 等),这会破坏动态表的一致性,导致代理需要重新编码头部,增加额外的延迟。
延迟诊断要点: 在服务网格中,如果 sidecar 代理(如 Envoy)修改了头部(例如添加
x-envoy-expected-rq-timeout-ms),会导致动态表条目失效,代理必须重新编码整个头部块。这增加了代理的 CPU 开销和延迟。建议在网格配置中启用 envoy.http_connection_manager 的 http2_protocol_options 中的 hpack_table_size 调整,以优化动态表大小。
3.5 总结:HTTP/2 延迟特征矩阵
| 机制 | 主要延迟收益 | 潜在延迟风险 | 网格诊断关注点 |
|---|---|---|---|
| 二进制分帧 | 解析效率高,帧处理快 | 帧重组逻辑复杂,可能引入代理延迟 | 代理的帧缓冲区大小和重组超时 |
| 多路复用 | 消除应用层队头阻塞,连接复用 | TCP 层队头阻塞仍然存在 | TCP 拥塞控制参数、丢包率 |
| 流优先级 | 关键请求优先处理 | 优先级配置错误导致请求饿死 | Envoy 的优先级策略和流调度 |
| HPACK | 头部体积大幅减小,传输快 | 动态表同步开销,代理修改头部破坏缓存 | 代理的头部修改频率、HPACK 表大小 |