应使用 sony/gobreaker 实现熔断,为每个依赖服务创建独立实例,区分错误类型、设置合理 sleepwindow,降级路径需加超时且避免新依赖,gin 中间件中统一处理 panic 和状态透传,并结合慢请求比例与错误率双指标监控及告警。

用 sony/gobreaker 实现熔断,别自己手写状态机
自己实现熔断器容易漏掉半开(Half-Open)状态的探测逻辑、失败计数重置时机、或并发安全问题。直接用 sony/gobreaker 是最稳妥的选择——它已通过大量生产验证,API 简洁,且默认支持 goroutine 安全。
常见错误是把熔断器当全局单例复用在所有下游服务上。实际中每个依赖(比如 user-service、payment-service)必须配独立熔断器实例,否则一个服务挂了会误熔其他服务。
- 初始化时按服务名区分:
breaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{Name: "payment-service"}) - 错误判定只认
error != nil,但 HTTP 4xx(如 404、400)通常不该触发熔断,需包装判断:if err != nil || resp.StatusCode >= 500 -
SleepWindow(熔断持续时间)别设太短(如 60s),影响恢复速度;建议从 30s 起调
降级逻辑必须走 context.WithTimeout,否则熔断形同虚设
熔断器只管“是否放行”,不负责“超时控制”。如果降级函数本身阻塞(比如查本地缓存卡住、或 fallback 调用另一个慢服务),整个请求仍会 hang 住,导致连接堆积、goroutine 泄露。
正确做法是在降级路径里也加超时,并确保 fallback 不再依赖任何外部服务:
- 降级函数内第一行就建带 timeout 的 context:
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) - fallback 数据优先用内存变量或 sync.Map 缓存,避免再查 Redis 或 DB
- 不要在降级里调
http.Get或db.Query—— 这等于把“降级”又变成“新依赖”
Gin 中间件里集成熔断,注意 panic 捕获和状态透传
很多人把熔断逻辑写在 handler 里,结果 panic 没 recover,导致整个 Gin server crash。必须在中间件层统一兜底。
同时,熔断打开时返回的错误类型要能被前端识别(比如统一 status code 503 + JSON 字段 "status": "degraded"),而不是裸抛 gobreaker.ErrTooManyRequests。
- 中间件开头加
defer func() { if r := recover(); r != nil { c.AbortWithStatusJSON(500, gin.H{"error": "internal error"}) } }() - 调用
breaker.Execute后,检查返回 error 是否为gobreaker.ErrOpenState,是则立即c.AbortWithStatusJSON(503, ...) - 别在中间件里做业务逻辑判断(如“用户 VIP 就不熔断”),该逻辑应放在
Execute的 run 函数内部,由熔断器统一决策
熔断指标监控不能只看错误率,要补上慢请求比例
仅靠错误率(ErrorPercentThreshold)触发熔断,在高延迟场景下反应滞后。比如下游响应从 100ms 慢到 2s,但错误率仍是 0%,熔断器不会动作,而调用方已积压大量 goroutine。
真实生产环境必须结合慢请求统计(例如耗时 >800ms 的请求占比 ≥30%),这需要自己埋点 + 判断,gobreaker 原生不提供该能力:
- 在
breaker.Execute的 run 函数外层包一层计时:start := time.Now(); defer func(){ if time.Since(start) > 800*time.Millisecond { slowCount++ } }() - 每 10 秒检查一次
slowCount / totalCount,超阈值就手动调breaker.HalfOpen()或breaker.Open() - 这个慢请求指标必须和错误率指标独立存储、独立判断,二者任一达标即触发保护
最容易被忽略的是:熔断器状态变更后,不主动通知运维或触发告警。至少得在 Open 状态进入时打一条带服务名的 ERROR 日志,并推送至监控系统。否则等用户投诉才发觉,已经晚了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











