必须为每个下游服务单独创建gobreaker.circuitbreaker实例,因其按依赖隔离设计,共用实例会导致a服务故障拖垮b服务;命名需带服务标识,配置timeout=15s、interval=30s~2m、maxrequests≥10,并配合context超时与prometheus监控。

为什么不能复用同一个 gobreaker.CircuitBreaker 实例
多个下游服务(比如 user-service 和 order-service)共用一个 breaker,等于把故障面强行拉平:A 服务一挂,B 服务的调用也全被拦住。这不是熔断,是误杀。
- 每个依赖必须独立初始化:
userCB := gobreaker.NewCircuitBreaker(userSettings)、orderCB := gobreaker.NewCircuitBreaker(orderSettings) - 实例应作为包级变量或注入到 client 结构体里,避免在 handler 函数内反复新建——每次新建都重置统计,熔断失效
- 别用全局单例管理所有依赖;gobreaker 本身不维护服务维度隔离,靠你代码组织来保证
gobreaker.Execute 的 fallback 什么时候真正触发
很多人以为传了 fallback 函数,超时或 error 就会自动走它。实际只在两种情况触发:gobreaker.ErrOpenState(熔断器处于 Open 状态)或主函数 panic。HTTP 超时返回的 error 不会进 fallback,但会被记为失败。
- 正确写法:显式判断
err == gobreaker.ErrOpenState,再手动调用降级逻辑 - fallback 函数必须快、无副作用、不发 HTTP、不查 DB、不调其他 breaker
- HTTP 场景下,fallback 返回的
*http.Response的Body字段不能为nil,否则上层io.Copy会 panic
HTTP 超时和 gobreaker.Settings.Timeout 必须错开设置
gobreaker.Settings.Timeout 只控制 Execute 内部函数返回的最大等待时间,它不中断底层 HTTP 连接。goroutine 会一直挂着,直到连接真正完成或被系统 kill。
- 所有
http.Client.Do必须传入带超时的ctx,且超时时间建议比gobreaker.Timeout短 20%~30% - 例如:HTTP 层设
context.WithTimeout(ctx, 800*time.Millisecond),breaker 的Timeout设1200*time.Millisecond - 漏掉
ctx透传,会导致连接堆积、goroutine 泄漏、长尾请求拖垮整个 handler
Gin 中间件里如何做 goroutine 数量熔断
等 runtime.NumGoroutine() 超过 5000 再响应,说明调度器已排队积压,此时再等超时或错误触发降级就晚了。必须前置检测+主动拦截。
- 在 Gin 中间件里每秒采样一次
runtime.NumGoroutine(),超过阈值(如 4000)立即开启熔断开关 - 熔断后所有新请求直接返回
http.StatusServiceUnavailable,不进业务逻辑,也不起 goroutine - 用
sync/atomic管理开关状态,避免加锁影响性能 - 禁用
runtime.NumGoroutine()频繁调用(如每次请求都查),它本身有性能开销;采样频率控制在 1Hz 即可
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











