gobreaker.execute必须仅包裹实际网络调用并显式返回错误,不能只包http client;需检查状态码、读取关闭body、禁止嵌套熔断器、fallback须签名一致且纯内存执行。

gobreaker.Execute 必须包裹完整调用链,不能只包 HTTP client
很多团队把 gobreaker.Execute 仅套在 http.Client.Do 外层,结果下游服务返回 500 时熔断器不触发——因为错误没被传出去。熔断器只捕获你函数里显式 return err 的错误,panic、未处理的 io.EOF、或忽略 resp.StatusCode 都会绕过统计。
- 必须检查 HTTP 状态码:若
resp.StatusCode >= 400,要主动return fmt.Errorf("http %d", resp.StatusCode) - 必须读取并关闭 body:否则连接复用失效,
gobreaker统计的“失败”只是连接层超时,不是业务失败 - 不要在
Execute内部再嵌套另一个gobreaker实例——状态冲突,ReadyToTrip判定失效
fallback 函数签名必须和原函数完全一致,包括 error 类型
写 func() (interface{}, error) 然后在 fallback 里硬编码 return "default", nil 是常见坑。调用方拿到 interface{} 后要做类型断言,一旦降级返回结构体字段少一个、时间戳类型错成 int64 而非 time.Time,就 panic。
- 原函数是
func GetUser(ctx context.Context, id int64) (*User, error),fallback 就得是func() (*User, error) -
*User里的字段必须全量初始化,哪怕填零值:&User{Name: "", Avatar: "", CreatedAt: time.Time{}} -
error不能用errors.New("fallback"),得复用原 service 的 error 类型,比如user.ErrNotFound,否则下游重试逻辑会误判
指标驱动降级必须异步刷新,不能每次请求都查 Prometheus
直接在 Execute 里跑 promapi.NewAPI(...).Query(...),QPS 上千时会把 Prometheus 拉挂,且 HTTP 调用本身又成了新的故障点。真正的智能降级靠的是本地缓存指标快照。
- 起一个独立 goroutine,每
10 * time.Second调一次rate(payment_service_http_errors_total[1m]) / rate(payment_service_http_requests_total[1m]) - 结果存进
sync.Map或原子变量,降级判断只读这个本地值 - 注意 Prometheus 默认抓取间隔是 15s,所以「1 分钟窗口」实际反映的是 45–60 秒前的状态,别拿它做秒级决策
人工开关优先级必须高于熔断器状态,且配置 key 要带服务名前缀
运维半夜收到告警说支付失败率飙升,想立刻切降级,却发现改了 etcd 里 degrade.enable 全局开关没用——因为代码里没读这个 key,或者读了但放在 gobreaker.StateOpen 判断之后。
- 降级判断顺序只能是:
config.GetBool("degrade.payment_service")→cb.State() == gobreaker.StateOpen→metricsCache.PaymentErrorRate > 0.3 - 配置中心 key 必须按服务隔离,比如
degrade.order_service、degrade.payment_service,避免一个服务降级拖垮全部 - 人工开关变更后需监听配置热更新,不能依赖重启——gobreaker 本身无 reload 接口,得在外层加一层 wrapper
真实场景里最易被忽略的,是 fallback 里调用了另一个可能失败的依赖(比如从 Redis 读默认头像),结果降级路径自己又崩了。降级逻辑必须是纯内存计算或只读本地缓存,任何外部 IO 都违背降级本意。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











