SuperAPI AI 工具集

arch-circuit-breaker

编程与开发

查看项目

更新于 10-16 16:26

技能介绍

用途

arch-circuit-breaker 用于保护服务免受性能下降或正在失败的依赖影响。它的核心目标是:在依赖不可用时停止继续调用它一段时间,让依赖有机会恢复,同时避免调用方服务被超时请求淹没,从而防止级联故障。资料给出的单行总结是“停止调用失败的依赖一段时间,让它恢复,并且让你的服务不被超时淹没”。

该技能建议将熔断器与舱壁、带退避的重试、超时组合使用,构成完整的弹性方案。

主要能力与核心内容

熔断器(Circuit Breaker):一个包裹依赖调用的状态机,包含三种状态。关闭(Closed)——调用正常放行,失败在滑动窗口中被计数,失败超过阈值后转为打开。打开(Open)——调用快速失败,不触达依赖,冷却期后转为半开。半开(Half-open)——少量探针调用放行,成功则回到关闭,失败则回到打开。熔断器通过本地吸收失败来防止级联故障,调用方快速失败(返回回退值或错误)而不是堆积在缓慢调用上。

舱壁(Bulkhead):按依赖隔离资源,使单个坏邻居无法拖垮其余部分。例如为每个下游服务使用独立线程池,为每个数据库使用独立连接池。其来源是船舶 hull 分舱,一处破损不至于沉没整条船。

超时(Timeout):每一次远程调用都必须有超时。没有超时,缓慢依赖会耗尽调用线程或协程并引发级联。超时应设为有限且保守的值,快速失败并重试优于无限等待。

带退避与抖动的重试:重试用于瞬时失败的再次尝试;退避是重试之间的等待(指数退避常见:100ms、200ms、400ms、800ms……);抖动是随机化等待时间,避免重试的惊群同步涌向正在恢复的依赖。重试只应对瞬时错误(超时、5xx、连接拒绝)触发,绝不应对 4xx 重试,因为那是永久性客户端错误。

回退(Fallback):依赖不可用时返回有用的内容而非错误,例如缓存值、默认值、“服务降级;部分数据”。资料强调回退策略是产品决策,而不是默认行为,需要有意选择。

库:不要自己实现。JVM 用 Resilience4j,.NET 用 Polly,Node 用 opossum。它们把上述模式组合成可配置的弹性管道。

使用时机

资料列出以下适用场景:服务间调用(REST、gRPC、消息代理)且下游可能失败或变慢;外部依赖(数据库、第三方 API、支付提供商)可能降级;任何单个缓慢依赖可能引发级联(线程阻塞其上、线程池被填满、服务停止接受请求)的系统;以及任何否则会写成“我会一直重试直到成功”的场合——那正是即将引发的级联。

不适用场景包括:所有依赖都在进程内,没有网络失败模式需要防护;简单同步失败应当直接向上传播给调用方(熔断器会隐藏失败,而这并不总是可取的);总量很低且缓慢调用不会级联,此时运维开销超过收益。

决策启发式

资料给出以下经验:每次远程调用先超时,再熔断器,再舱壁,在预期有瞬时错误处加入重试。失败阈值要低到快速反应,又高到能吸收随机抖动,可以从“20 次调用中 50% 失败”起步再调优。冷却时间:对偶尔抖动的健康依赖设为数十秒;对需要人工介入的依赖设为数分钟。始终为退避加入抖动,同步重试会造成惊群。回退是产品决策,团队的 PM 应当了解“降级模式”是什么样子。

反模式

资料列出的反模式包括:没有超时(挂起调用淹没线程池,是级联故障最常见的原因);重试没有退避(立即让挣扎中的依赖负载翻倍);对 4xx 重试(把永久错误重试成永久性反复失败);熔断阈值调在错误的维度上(失败率是按窗口的,脱离窗口上下文的原始失败计数没有意义);熔断器没有回退(打开状态意味着快速失败,但如果调用方没有 Plan B,你只是给失败换了个标签);手写熔断器(Resilience4j / Polly 已经解决了多年的边界情况,不要自己写)。

相关技能与参考

相关技能包括:arch-microservices(服务之间熔断器必不可少)、arch-saga(调用其他服务的 saga 步骤需要此保护)、arch-event-driven(失败模型不同,熔断器模式用于同步调用)、kp-structured-concurrency(Kotlin 的 withTimeout 覆盖进程内协程代码的超时部分)。

参考书目:Michael Nygard 的《Release It!》(Pragmatic Bookshelf),弹性模式的经典著作,熔断器即在此书中提出;Richards 与 Ford 的《Fundamentals of Software Architecture》微服务与分布式架构章节;Resilience4j 文档作为具体实现参考。