gobreaker 是 Go 熔断事实标准,轻量线程安全,需包装 RoundTrip 而非外层套 Execute;ReadyToTrip 和 Timeout 必须按 P95 耗时与错误类型调优,避免误熔断或不恢复。

直接用 gobreaker,别手写状态机
Go 生态里 gobreaker 是事实标准,go-zero、kratos、grpc-go 官方示例都用它;自己用 sync.Mutex + time.Timer 写,大概率在并发激增时漏统计失败、半开态卡死,或误把 404 当故障熔断。
-
gobreaker轻量单文件、无依赖、线程安全,cb.Execute原生支持context.Context - 停更的
hystrix-go不兼容 Go module,超时和context冲突,中断时 panic 而非返回 error,线上已基本淘汰 - 初始化只需
go get github.com/sony/gobreaker,全局复用一个实例即可,不用每个请求 new 一个
HTTP 客户端必须包装 RoundTrip,不是套 cb.Execute 外层
很多人想“给整个 http.Client 加熔断”,但 http.Client 没法拦截单次调用——真正要熔断的是「一次具体请求」,必须侵入到底层 Transport.RoundTrip。
- 错误做法:
cb.Execute(func() { http.DefaultClient.Get(...) })→ 超时控制失效、连接池复用被破坏、ctx传不进底层 - 正确做法:写个自定义
RoundTripper,在RoundTrip方法里调用cb.Execute,透传原始*http.Request和context - 闭包陷阱高发:循环中闭包捕获
req变量会导致所有请求共用同一地址,出现http: Request.Write on Body closedpanic - 正确闭包写法:
func(req *http.Request) func() (interface{}, error) { return func() { ... } }
ReadyToTrip 和 Timeout 是最常配错的两个参数
90% 的“熔断不生效”或“一直不恢复”,问题就出在这俩配置上。默认值只适合本地调试,上线必调。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
Timeout(熔断持续时间):建议设为下游 P95 耗时的 2–3 倍;默认 60s 太长,下游已恢复,你的服务还在拒流 -
ReadyToTrip(触发熔断逻辑):默认是“最近 100 次失败率 > 60%”,但真实场景要过滤错误类型——比如只把net/http.ErrTimeout、context.DeadlineExceeded算失败,sql.ErrNoRows或404必须放过 -
MaxRequests(半开试探请求数):设 3–5 较安全;设 1 恢复慢,设 100 可能压垮刚恢复的下游 - 低频服务(QPS RequestVolumeThreshold 调小,否则永远达不到统计窗口,熔断器形同虚设
降级逻辑必须纯内存、无副作用,且不能嵌套外部调用
cb.Execute 返回 gobreaker.ErrOpenState 时,你得立刻返回可用结果,而不是再查缓存 Redis、再调一次配置中心——那等于绕过熔断保护。
立即学习“go语言免费学习笔记(深入)”;
- HTTP 接口降级可返回兜底 JSON、静态文案、或上一次成功响应(加
staleheader 标识) - 用户服务类可降级为只读:允许
GET /user,拒绝POST /user/avatar - 降级函数里禁止调用另一个可能熔断的服务,也禁止阻塞操作(如
time.Sleep、大循环) - 别忘了在
OnStateChange回调里打点或记日志,否则熔断跳闸了你根本不知道
真正难的从来不是加一行 cb.Execute,而是想清楚什么算“失败”、什么时候该“试探恢复”、以及降级结果能不能真扛住流量——这些没法靠库自动解决,得贴着业务逻辑一条条对齐。

















