应全局复用 gobreaker 实例,正确处理 ErrOpen 降级,避免在 OnStateChange 中阻塞,HTTP 场景优先封装 RoundTripper,指标需手动通过回调+原子变量维护。

Go 里用 gobreaker 做熔断,别自己手写状态机
自己实现熔断器容易漏掉超时重置、滑动窗口计数、并发安全这些细节,gobreaker 是经过生产验证的轻量库,直接用它最稳。
常见错误是把 gobreaker.NewCircuitBreaker 放在函数内反复创建——这会导致每个调用都独立统计,完全失去熔断意义。必须全局复用一个实例。
- 初始化一次,存在包变量或注入到结构体中
- 配置
ReadyToTrip函数时,别用耗时操作(比如查 DB),它会在每次请求前被调用 -
OnStateChange回调适合打日志或发告警,但别在里面阻塞主流程 - 默认使用
gobreaker.Settings{Timeout: 60 * time.Second},实际建议设为后端依赖的 P95 耗时 × 2~3 倍
降级逻辑必须和熔断器解耦,不能藏在 Execute 的闭包里
很多人把 fallback 写成 cb.Execute(func() (interface{}, error) { ... }) 里的 recover() 或 if err != nil 分支,结果熔断打开后 fallback 也跟着失败——因为 Execute 只负责执行主逻辑,不接管降级。
正确做法是:先调用 cb.Execute,捕获返回的 gobreaker.ErrOpen,再单独走降级分支。
-
gobreaker.ErrOpen表示熔断器处于open状态,此时应立刻返回缓存值、默认值或兜底数据 - 不要依赖
Execute的 error 类型做降级判断,它也可能返回业务错误或网络超时,和熔断状态无关 - 降级逻辑本身也要加超时控制,避免拖慢整个请求链路
HTTP 客户端调用场景下,RoundTripper 封装比手动 wrap 每个 Do 更可靠
如果项目里大量用 http.Client 调第三方服务,逐个在 Do 外套 cb.Execute 容易漏、难维护。更干净的方式是实现自定义 RoundTripper。
原理很简单:把熔断器嵌入 RoundTrip 方法,在发起真实请求前检查状态,打开时直接返回预设响应;关闭/半开时才透传给底层 http.Transport。
- 注意要复制原始
*http.Request,避免修改影响后续中间件 - 返回的
*http.Response必须有Body字段(哪怕是个空io.NopCloser(bytes.NewReader(nil))),否则上层会 panic - 这种封装对
net/http标准库完全透明,不影响httptrace或其他中间件
gobreaker 默认不支持指标上报,但 OnStateChange 是埋点唯一入口
想对接 Prometheus 或 Datadog?gobreaker 没内置 metrics,也没 WithMetrics 选项。所有可观测性都得靠 OnStateChange 回调手动打点。
容易踩的坑是只记录状态切换事件,却没同步更新当前请求数、失败数、成功率等瞬时指标——这些得你自己用 sync.Map 或原子变量维护。
- 回调里拿到的
to参数是新状态(gobreaker.StateClosed/Open/HalfOpen),from是旧状态 - 别在回调里做远程上报(比如 HTTP 请求),会拖慢状态切换,甚至引发死锁
- 如果用了多个熔断器实例,建议用
name字段区分,方便后续聚合
真正麻烦的是半开状态下的试探请求怎么设计:既不能全放行压垮下游,也不能只放一个导致恢复太慢。这个阈值和探测频率得结合你的服务 SLA 来调,没有通用值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











