Cluster / LB 已经能选出一台「健康」主机,但现场排障里大量 503 并不来自「路由指错 cluster」,而来自 连接池耗尽、熔断打开、outlier 把主机剔出可调度集合。本文钉这三层资源语义,说明它们如何在「RDS/CDS 看起来正确」时仍让 Router 对外失败,并为 xDS 资源树 里「有 cluster 无可用 endpoint」的配置依赖做铺垫。
本文是「Envoy / 数据面代理内核」系列第 8 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 7 篇 · Cluster / LB 选主层次 第 8 篇 · 连接池 / 熔断 / outlier 限额如何卡死上游 第 9 篇 · xDS 资源树 LDS→EDS 依赖 上一篇:Cluster 与负载均衡 · 下一篇:xDS 资源树
版本锚定:Envoy v1.39.0 Connection pooling、Circuit breaking、Outlier detection。无伪造 stats 输出。
本篇不展开:LB 算法与 subset 键设计(第 7 篇)、xDS 推送顺序(第 9 篇)、全局/本地 rate limit 扩展(第 13 篇)。只回答「选主之后,资源限额与被动驱逐如何让请求失败」。
一、失败轴:不是只有「上游挂了」
Router 在选中 cluster
后向连接池要一条可发流的上游连接。任一环节拒绝,下游常见表现是
503,并可能带
x-envoy-overloaded(熔断 / maintenance 路径,见
Router 文档)。归因前先分轴:
| 轴 | 典型计数 / 信号 | 含义 |
|---|---|---|
| 路由 | no_route / no_cluster |
根本没进到合法上游名 |
| 池 / 熔断 | upstream_cx_overflow、upstream_rq_pending_overflow、upstream_rq_retry_overflow
等 |
资源配额打满 |
| Outlier | 驱逐事件日志 / 主机从 LB 集合消失 | 被动认为该主机异常 |
| 真上游错误 | 上游 5xx(未必变 503) | 已出池、已收到响应 |
本篇关注中间两行。
flowchart TD
router["Router"] --> lb["LB picks host"]
lb --> cb{"Circuit breaker<br/>room?"}
cb -->|"no"| fail503["503 / overloaded"]
cb -->|"yes"| pool["Conn pool acquire"]
pool -->|"pending full"| fail503
pool -->|"ok"| up["Upstream attempt"]
up --> od["Outlier may eject host"]
二、连接池:协议抽象与「有多少个池」
官方 Connection pooling:HTTP 流量使用抽象连接池,屏蔽 HTTP/1.1 与 HTTP/2·3 是否真多路复用。
| 协议 | 行为要点 |
|---|---|
| HTTP/1.1 | 按需建连;不用 pipelining,一连接同时服务一请求,避免上游断开时重置过多下游 |
| HTTP/2 / HTTP/3 | 多路复用直至 max concurrent streams /
max requests per connection;忙则标
busy,可再建连直至熔断上限 |
池的数量不是「每 cluster 一个」:
- 每个 Worker 线程各自维护池。
- 每主机、每协议(未用 ALPN 合一协商时)、以及不同 transport socket / 可哈希且标记共享的 downstream filter state,都可能再拆池。
- 文档例子:2 线程 ×(HTTP/1 + HTTP/2)支持 → 至少 4 个池。
含义:电路熔断的「最大连接数」是 跨 Worker 共享的
cluster
限额(最终一致,允许竞态短暂超过),但池实例是每线程的——排障时看全局
upstream_cx_active
的同时要意识到局部池状态并不在一个锁下。
主动或被动健康检查导致主机从 available → unavailable 时,该主机相关连接池连接会被关闭;主机回到旋转后再建新连接,以规避坏 ECMP 路径等粘滞问题。
三、Circuit breaking:分布式限额
官方强调:Envoy 在网络层施加完全分布式(各代理不协调)的熔断,默认开启且带温和默认值(例如每 cluster 1024 连接量级);要「关掉」需把阈值调到允许的最大值。
按 cluster × priority 跟踪的典型限额:
| 限额 | 溢出计数(文档名) | 直觉 |
|---|---|---|
| Max connections | upstream_cx_overflow |
到该 cluster 所有主机的连接上限;drain 中连接仍计数 |
| Max pending requests | upstream_rq_pending_overflow |
等不到就绪连接时的排队上限 |
| Max requests | upstream_rq_active_overflow |
集群飞行中请求上限 |
| Max pending retries | upstream_rq_retry_overflow |
飞行中重试上限;更推荐 retry budget |
| Max concurrent connection pools | upstream_cx_pool_overflow |
池实例数上限(如 original_dst 可制造大量池) |
注意文档细节:即使连接熔断已溢出,Envoy 仍保证 LB
选中的主机至少有一条连接——因此
upstream_cx_active
可以高于「最大连接」配置,理论上界与「每 endpoint ×
每池」有关。Worker 之间共享限额但是 eventually
consistent,短时超过阈值可能发生。
对 HTTP/2,若未限制并发流,请求多路复用在同一连接上,pending 熔断往往只在尚无连接时触发——不要用 HTTP/1 直觉硬套。
路由层的 priority
routing(default /
high)会使用不同的连接池与熔断设置:即便
HTTP/2,到同一上游主机也可能是两条物理连接。这与 EDS 的
priority 档位相关但配置面不同——一个是请求优先级,一个是
endpoint 拓扑档;两者都会放大「看起来同一个
cluster」下的资源记账维度。
四、Outlier detection:被动摘除
Outlier 是 passive health checking:根据连续失败、成功率、延迟等把主机标为不健康并移出正常 LB 集合(panic 场景除外)。主动 HC 可并行;过滤器必须上报错误——HTTP Router、TCP Proxy、Redis、Thrift 等支持。
错误分两类(可
split_external_local_origin_errors):
- 外部:上游事务错误(如 HTTP 5xx)。
- 本地:Envoy 侧超时、reset、连不上等。
默认不拆分时,本地与外部计入同一桶,对比
consecutive_5xx 等阈值。驱逐算法要点:
- 判定 outlier。
- 若已驱逐比例达到
max_ejection_percent,则不再驱逐。 - 驱逐时长 ≈
base_ejection_time × 连续驱逐次数,封顶max_ejection_time;恢复后乘数随健康间隔递减。
检测类型包括 consecutive 5xx / gateway
failure(502–504)/ local origin failure、success
rate、failure percentage、以及 x-envoy-degraded
的 degraded(仍在旋转但降权)。共享同一 cluster
的多条 filter chain 会共享驱逐结果——一条链因 HTTP
5xx 踢掉的主机,另一条 TCP 链也会受影响。
gRPC 路径上,outlier 默认把 grpc-status 映到
HTTP 语义再计入;若业务用非 5xx 的 HTTP 码表示失败,需在
cluster extension_protocol_options 的 outlier
映射里显式把匹配响应报成 5xx,否则被动检测会当成成功。
与主动 HC:默认成功主动检查可
uneject;若探活不代表数据面业务成功,可能过早拉回坏主机,可关
successful_active_health_check_uneject_host。
五、排障口诀:路由对仍 503
- 先排除
no_route/no_cluster(第 6 篇)。 - 看 cluster 熔断剩余与
*_overflow计数是否在爬升。 - 看 outlier 是否把多数主机驱逐到只剩空集或 panic 行为。
- 再看 subset / priority 是否把集合收成空(第 7 篇)。
- 最后才是上游进程真死——那往往是 5xx 透传或连接失败,而不一定是 Envoy 合成的 overloaded 503。
配置层:熔断过小 + 重试无预算 = 自激振荡;outlier 过敏 + 小集群 = 一次抖动驱逐过半主机。调参必须按 priority 分流的独立阈值 理解,不能只看「cluster 平均值」。
可选开启 outlier 事件日志(Cluster manager
级配置,protobuf
OutlierDetectionEvent):全局计数只能说明「有驱逐」,不足以回答「哪台、因为哪种检测」。生产排障应优先事件流,再回看
consecutive_* / success rate
阈值是否与错误分类(是否 split local/external)一致。
六、谱系、争论与开放问题
谱系:熔断作为分布式系统快速失败手段,在微服务代理层被做成每跳强制配额(Envoy circuit breaking 文档开篇立场),避免每个应用各自实现不一致。Outlier 延续「被动探测 + 短暂隔离」运维传统,与主动探活组成完整健康方案。
争论:默认熔断阈值是否应更保守。过宽等于未保护;过窄在扇出服务上制造系统性 503。官方选择「默认开启 + 温和默认 + 可观测剩余配额」,把精细度留给运维——这是工程默认,不是理论最优。
开放问题:跨 Worker 最终一致限额在超高并发短连接场景下的超调幅度,缺乏本系列可引用的本机实测;大规模 EDS 抖动下 outlier 与主动 HC 谁先稳态,见第 9–11 篇与排障篇。
七、参考资料
规范 / 官方文档(A)
- Envoy v1.39.0,Connection pooling。
- Envoy v1.39.0,Circuit
breaking(含
x-envoy-overloaded说明)。 - Envoy v1.39.0,Outlier detection(驱逐算法与检测类型)。
- Envoy v1.39.0,Router(overloaded 头与 retry overflow 统计)。
源码(A)
envoyproxy/envoytag v1.39.0:source/common/upstream/(conn pool、circuit breaker、outlier)。
站内
实验台账
- 本篇不粘贴
/stats输出;计数名称以官方文档为准,实跑后再写入排障篇。
八、小结
- 连接池按线程 / 协议 / socket 选项分池;熔断限额跨 Worker 共享但最终一致。
- Circuit breaker 溢出是「路由正确仍 503」的第一嫌疑;重试有独立上限与 budget。
- Outlier 被动摘除主机,可与主动 HC 叠加;共享 cluster 则共享驱逐后果。
- 资源对象本身由 xDS 树挂出——下一篇讲 LDS→RDS→CDS→EDS 命名与依赖顺序。
→ 系列目录 · 上一篇:Cluster 与负载均衡 · 下一篇:xDS 资源树
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【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 的典型失败模式。
【Envoy 数据面】Network filter 与 Connection:字节流、水印与 TCP Proxy / HCM 分叉
在 FilterChain 已选定之后,说明 network filter 如何在连接字节流上工作、读写水印如何反压、连接生命周期事件,以及 TCP Proxy 相对 HTTP Connection Manager 的路径分叉。
【Envoy 数据面】HCM 与 Codec:HTTP/1·2·3、流生命周期与编解码错误
说明 HttpConnectionManager 如何把字节变成协议无关的 stream 事件;HTTP/1/2/3 codec 的职责边界;流与连接生命周期差异;以及 codec 错误与半关闭语义的失败模式。