上一篇把路径分到
http_connection_manager。本篇进入 L7
入口:HCM 做什么、codec 做什么、stream 与 connection
为何不能混称。 把 HTTP/1 流水线、HTTP/2
多路复用、HTTP/3/QUIC 都当成「同一个 socket
上的同一个请求对象」,会在超时、重置与连接复用上误判。
依据 v1.39.0 HTTP connection management:HCM 是内置 network filter;codec API 把不同线协议译成协议无关的 stream / request / response 形态。
本文是「Envoy / 数据面代理内核」系列第 5 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 4 篇 · Network Filter 字节流与通向 HCM 的分叉 第 5 篇 · HCM Codec 编解码、流生命周期、错误 第 6 篇 · HTTP Filter Router 过滤器顺序与终端路由 第 8 篇 · Conn Pool 上游协议池与复用
版本锚定:Envoy v1.39.0 HTTP connection management、HTTP/3 overview、Connection pooling;源码
source/common/http/、source/extensions/filters/network/http_connection_manager/。
一、HCM:HTTP 连接的「操作系统」
官方定位:HTTP Connection Manager 把原始字节翻译成 HTTP 级消息与事件(headers / body / trailers received 等),并承担所有 HTTP 连接与请求共性工作:access log、request ID、tracing、首部处理、路由表管理、统计等。
路由表来源二选一:静态,或 RDS 动态下发。重试插件、内部重定向、超时等也挂在 HCM / 路由配置上——细节第 6 篇展开;本篇只钉「事件从哪来」。
安全相关:HCM 会做 header sanitizing;不可信下游首部清理与「过滤器再改一次首部」的交互,在内部重定向文档里有明确警告(清理只完整适用一次等)——配置 header 操作时要当一等风险。
HCM 持有的路由表是该 HCM 实例的视图:静态嵌在 typed_config 里,或经 RDS 订阅。多 listener / 多 HCM 并不自动共享一张表——「改了 RDS 为何这个口没变」首先核对哪个 HCM 订了哪个 route config name,而不是先怀疑 TLS 快照失灵。
二、Codec:线协议 → 协议无关 stream
设计目标(官方原文要点):Envoy HTTP 支持首先按 HTTP/2 多路复用代理来设计;内部用 HTTP/2 术语描述组件——一次请求/响应发生在一条 stream 上。
flowchart LR
Wire["Wire bytes"] --> C1["HTTP/1 codec"]
Wire --> C2["HTTP/2 codec"]
Wire --> C3["HTTP/3 codec"]
C1 --> Agg["Protocol-agnostic streams"]
C2 --> Agg
C3 --> Agg
Agg --> HCM["HCM + HTTP filters"]
| 协议 | Codec 职责摘要 | 对上层的「样子」 |
|---|---|---|
| HTTP/1.1 | 把串行 / pipelining 译成类 HTTP/2 的 stream 视图 | 多数代码不感知起源协议 |
| HTTP/2 | 帧、流 ID、HPACK、SETTINGS / GOAWAY 等 | 原生多路复用 |
| HTTP/3 | QUIC 上的 HTTP;与 TCP 路径分叉 | 仍映射到同一套 stream 事件 |
codec_type(如
AUTO)决定如何选下游 codec;上游协议由 cluster
的 HTTP 协议选项 / 连接池决定(Connection
pooling)。下游是 h2、上游是 h1
是常态:stream 在 HCM 内扇出到不同池。
WebSocket、CONNECT、TCP/UDP over HTTP
等升级路径见官方 HTTP
upgrades——本篇不展开,只强调:它们仍先经过「能说话的
codec + HCM」,不是另一条旁路 Listener 魔法。
HCM 还统一做 request ID 生成与 tracing 注入点:编解码成功之前,这些字段往往尚未稳定。因此「没有 request id 的断开」更可能是 listener / network / codec 前期失败,而不是 Router 没打日志。
三、Stream vs Connection
| 对象 | 是什么 | 销毁/结束常见条件 |
|---|---|---|
| Connection | 下游(或上游)传输会话;HTTP/1 常 1 请求序列,H2/H3 多 stream | 空闲超时、GOAWAY、reset、对端关闭、排水 |
| Stream | 一条请求—响应逻辑通道 | 见下方 lifecycle;错误或超时可提前结束 |
HTTP lifecycle(v1.39.0)要点:
- 代理在下游 codec 成功解出请求头后开始。
- 流何时销毁依赖上游协议与是否启用
independent half-close:
- 若启用且上游为 HTTP/2 或 HTTP/3:请求与响应两侧都达 end-of-stream,且响应为成功 2xx 时销毁 stream。
- HTTP/1 上游或未启用独立半关闭:响应完成(达 end-of-stream)即可销毁;若此时请求未完成则 reset stream。
- 错误、超时、对端 reset H2/H3 stream 可使代理提前停止。
排障含义:H2 下「连接还在、单 stream 已 reset」与 H1 下「连接直接断」不是同一症状;日志里应区分 connection 级与 stream 级字段。
把 lifecycle 两条分支写成检查表:
- 上游是 H1 吗?→ 响应结束即可拆 stream,未完成的请求侧可能被 reset。
- 上游是 H2/H3 且开启 independent half-close 吗?→ 两侧 end-stream + 成功响应才拆。
- 是否超时 / 对端 reset?→ 代理可提前停,与「正常生命周期」并列,不是互斥。
漏掉第 1 条去调 H2 半关闭参数,是常见的配置空转。
四、Codec 与协议错误模式
| 症状 | 可能落点 | 说明 |
|---|---|---|
| 连接建立后立即协议错误 | 下游 codec 选错 / ALPN 与 chain 不匹配 | 回到第 3 篇 SNI/ALPN |
| 部分请求 503,连接仍活 | 单 stream 失败或上游池限额 | 连接≠全部 stream 健康;见第 8 篇 |
| 上传未完成响应已结束 | half-close / H1 上游生命周期 | 对照官方 lifecycle 两条分支 |
| H3 回落 H2/H1 | UDP 被拦或 Alt-Svc 缺失 | Connection pooling / HTTP/3 文:UDP 阻断常见;自动选择有 TCP 故障转移语义 |
| 首部相关安全问题 | sanitizing 与 filter 改写顺序 | 勿假设「每个 filter 都看到原始下游头」 |
上游池侧(官方 Connection pooling):H1 池不靠 pipelining 绑多请求到一连接以免级联 reset;H2/H3 池在 max concurrent streams、GOAWAY、每连接请求上限下排水并开新连接。每个 Worker 各自维护 pool——与第 2 篇 shared-nothing 一致。多协议 cluster 在非 ALPN 场景下可能每协议一池;再乘 Worker 数,池实例数会快速上升——这是内存与 FD 规划问题,不是「开了 h2 就一定更省连接」。
自动协议选择(正向代理常见
AutoHttpConfig):TCP + ALPN 在 H2/H1
间择优;掺 H3 时依赖 alternate protocol 缓存 / Alt-Svc
等广告,并可能对 QUIC 与 TCP
做短窗口并行尝试。机制允许回落;观测上必须能区分「从未尝试
H3」与「尝试失败后回落」,否则会把网络 UDP
策略误判成「Envoy 不支持 H3」。
五、谱系、争论与开放问题
谱系:HTTP/2(RFC 7540 一系)把多路复用变成代理内核的默认心理模型;Envoy 用 codec 把 H1「抬」到同一抽象,避免为三套协议写三套 filter。HTTP/3/QUIC 把传输从 TCP 挪到 UDP,强制数据面出现第二条监听与故障转移经济学。
争论:
- 「内部一律 HTTP/2 语义」简化了 filter 作者心智,但 H1 半关闭与错误传播仍泄漏到运维表面——生命周期文档本身就是妥协说明书。
- Mesh 默认互操作偏 H2,边缘代理面对浏览器 H1/H2/H3 混部;AUTO + ALPN 与显式分链哪种更可审计,取决于是否要在 FilterChainMatch 层切开失败域。
开放问题:
- H3 在中间盒 / 企业网上的可达性与「假失败」观测(官方仍强调 UDP 阻断与回落路径的生产验证)。
- 独立半关闭默认策略与上游协议矩阵,如何在平台层给统一超时语义(避免每条 route 一套隐式假设)。
- Codec 错误相对 HTTP filter 错误的可观测字段是否足够——第 14、15 篇收束。
工程间隙:规范描述帧与流;生产还要加连接池熔断、重试、outlier。HCM 只保证「事件进 filter 链」;会不会打到健康上游是第 7–8 篇。
对本系列读者的收束句:第 3 篇选对链、第 4 篇选对终点、第 5 篇选对 codec 视图——三者任一失败,第 6 篇的 Router 再正确也救不了。最小联调路径因此是 03 → 04 → 05 → 06,而不是从 RDS 路由表倒查。
六、下一篇预告
Codec 解出 headers 之后,请求进入 HTTP filter 链;Router 通常是终端过滤器,负责选 cluster、触发上游请求。过滤器顺序、短路、与重试/超时落点见第 6 篇。
超时与重试在文档中分属 HCM / route 配置;本篇不展开数值默认值。需要记住的只有:超时时钟挂在哪一层对象(连接 vs stream vs 路由尝试)上,决定你该看哪类 stats——具体字段表留给第 6、15 篇。
七、参考资料
规范 / 官方文档(A)
- Envoy Proxy, HTTP connection management(protocols、lifecycle、sanitizing、RDS),docs v1.39.0。
- Envoy Proxy, Connection pooling(H1/H2/H3 池性质、自动协议选择)。
- Envoy Proxy, HTTP/3 overview;HTTP upgrades(边界)。
- 相关 IETF:HTTP/2 / HTTP/3 / QUIC 规范族(机制背景;实现以 Envoy 文档为准)。
源码(A)
envoyproxy/envoytag v1.39.0:source/common/http/、source/extensions/filters/network/http_connection_manager/。
站内对照
实验台账
- 无 codec 基准测试数字;不粘贴未执行的 admin 输出。
八、小结
- HCM 把字节变成 HTTP 事件,并承载日志、追踪、路由表、统计等共性能力。
- Codec 屏蔽 H1/H2/H3 线差异;上层以 stream 思维工作,但仍须按协议理解销毁与 reset。
- Connection 存活 ≠ 所有 stream 成功;half-close 与上游协议决定流何时结束。
- 编解码与 ALPN/选链错误要先排除,再怀疑 Router——下一篇进入 HTTP filter 与 Router 终端。
→ 上一篇:Network filter 与 Connection · 系列目录 · 下一篇:HTTP filter 链与 Router
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Envoy 数据面】Network filter 与 Connection:字节流、水印与 TCP Proxy / HCM 分叉
在 FilterChain 已选定之后,说明 network filter 如何在连接字节流上工作、读写水印如何反压、连接生命周期事件,以及 TCP Proxy 相对 HTTP Connection Manager 的路径分叉。
【Envoy 数据面】HTTP filter 链与 Router:顺序语义、终端过滤器与重试超时落点
钉住 HCM 内 HTTP filter 的 decoder/encoder 顺序、Router 作为终端过滤器的含义,以及 retry/timeout 挂在路由与 Router 上的语义;说明「Router 之后再挂 filter」为何无效,并为 Cluster 上游选型铺路。
【Envoy 数据面】Main / Worker 与配置快照:事件循环、TLS 与几乎无锁热路径
钉住 Envoy 单进程多线程模型:Main 管 xDS/Admin,Worker 绑连接终生;Event::Dispatcher 与 Thread Local Storage 如何把配置变成每线程可读快照,以及快照解决什么、解决不了什么。
【Envoy 数据面】Listener 与 FilterChainMatch:accept、listener filters 与选链失败
拆解 Envoy accept 之后到 network filter 之前:listener filters 如何 enrich 元数据,FilterChainMatch 如何按 SNI/ALPN/目的地址选链,以及选错 chain 的典型失败模式。