网关层不做熔断,熔断必须实现在微服务调用方;需用context控制http超时、配置http.transport、手动转换非2xx为error、按滑动窗口错误率而非连续失败数判断熔断、合理设置interval与timeout、fallback须纯内存无依赖。

网关层不做熔断——这是最常被误用的起点。 真实生产环境里,第三方延迟抖动必须在调用方(即你的微服务)内部拦截,网关只能做粗粒度限流或路由降级,没法感知下游真实错误率、无法区分超时与业务失败、更不能按方法维度隔离故障。
gobreaker.Execute 为什么总卡死,不触发熔断
根本不是熔断器的问题,是 HTTP 调用本身没被 context 控制住。gobreaker 只看 Execute 闭包返回的 error 是否为 nil,而默认 http.Client 在超时后可能返回 nil, nil 或自定义 timeout error(比如 net/http: request canceled),gobreaker 默认不认为这是失败。
- 必须用
context.WithTimeout构造请求,并显式传入:req = req.WithContext(ctx)或直接用http.NewRequestWithContext -
http.Transport必须配置DialContext、ResponseHeaderTimeout、IdleConnTimeout,否则 DNS 解析、TCP 建连、TLS 握手阶段完全不受 context 约束 - 绝对不要复用
http.DefaultClient——它零超时配置,一个慢请求会拖垮整个连接池 - 第三方返回非 2xx 状态码时,要手动转成 error:
if resp.StatusCode >= 400 { return nil, fmt.Errorf("HTTP %d", resp.StatusCode) }
ReadyToTrip 判断必须基于滑动窗口错误率,而非连续失败数
写成 counts.ConsecutiveFailures > 3 是线上高频误熔断根源。低 QPS 服务一次网络抖动就连续失败 3 次,立刻开闸;高 QPS 服务瞬时毛刺又可能被淹没。
- 推荐写法:
counts.Requests >= 20 && float64(counts.TotalFailures)/float64(counts.Requests) > 0.6 -
Interval设太短(如5 * time.Second)会导致窗口过小,无法反映真实稳定性;建议设为业务 SLA 的 3–5 倍(例如第三方接口承诺 P99 ≤ 1200ms,窗口至少设 5 秒) -
Timeout(即 Open 状态持续时间)别和 context 超时混用:前者单位是秒级(如30 * time.Second),后者是毫秒级(如800 * time.Millisecond);设成一样会导致半开探测过于激进,反复震荡
fallback 必须独立、纯内存、无外部依赖
gobreaker 不提供 fallback,所有降级逻辑得塞进 Execute 的闭包里。一旦 fallback 自己也调第三方或走 DB,等于把熔断器绕过去了。
- fallback 返回值应来自本地缓存、静态配置或兜底结构体,例如:
return &User{Name: "default"}, nil - 别在 fallback 里重试原请求——这违背熔断语义,且可能放大下游压力
- 如果第三方提供健康检查 endpoint,可在半开状态试探时优先调它,但不要把它当 fallback 主路径
- 对 gRPC 调用,需手动将
codes.DeadlineExceeded等 status code 转成 error,否则 gobreaker 收不到失败信号
真正难的不是配参数,而是厘清「谁该对哪段延迟负责」:网关只管流量整形,熔断必须落在具体 client 层;超时控制是熔断的前提,不是替代品;错误率统计窗口和业务节奏必须对齐,否则再准的算法也会误伤。











