gobreaker半开状态需调用方主动触发试探请求,超时后仅允许试探,不自动发起;成功则恢复闭合,失败即重置为开启。

gobreaker 半开状态怎么触发试探请求
断路器不会自动“闭合”,它只会在 Timeout 时间后从 Open 进入 HalfOpen,但此时仍需你主动发起一次请求,才能触发试探逻辑。gobreaker 不会后台偷偷发请求,所有试探行为由调用方控制。
常见错误是以为设了 Timeout: 60 * time.Second 就能自动恢复——其实只是“允许你开始试”,没人帮你调用服务。
- 必须在
HalfOpen状态下调用cb.Execute(),哪怕只是模拟一次健康检查 - 如果试探失败(哪怕只有一次),立刻切回
Open;成功则根据ReadyToTrip和内部计数决定是否切回Closed - 别在定时器里硬循环重试:用
time.AfterFunc或外部调度器触发一次试探即可,避免并发打爆下游
ConsecutiveFailures 和窗口失败率的区别
gobreaker.Counts.ConsecutiveFailures 统计的是**连续失败次数**,不是滑动窗口内的总失败数。这是新手最容易误解的一点:它不看“过去 10 秒失败了 8 次”,而只看“最近连续失败了几次”。
这意味着:只要中间有一次成功,ConsecutiveFailures 就归零。所以如果你的 ReadyToTrip 写成 counts.ConsecutiveFailures >= 3,那必须是三次失败连着发生,才熔断。
- 想按失败率熔断?得自己算:
float64(counts.TotalFailures) / float64(counts.TotalRequests),但注意TotalRequests包含成功+失败,且只在当前窗口内有效 - 滑动窗口统计靠
gobreaker.NewCircuitBreaker默认启用的gobreaker.DefaultSettings中的BucketDuration和BucketCount控制,不是靠ConsecutiveFailures - 连续失败太敏感?可改用
MaxRequests+ 自定义ReadyToTrip配合延迟重试,避免毛刺误熔
为什么 cb.Execute 返回 (interface{}, error) 而不是原类型
cb.Execute 是泛型前时代的通用封装,它不知道你里面调的是 HTTP 还是数据库,所以统一返回 interface{} 和 error。你不做类型断言,就拿不到真实响应。
典型错误是直接写 resp, err := cb.Execute(...) 然后当 resp 是 *http.Response 用,结果 panic:interface{} is nil。
- 正确做法:先判断
err == nil,再用类型断言,例如data, ok := resp.([]byte) - 如果执行体返回
nil, nil,resp就是nil,不是空切片或零值结构体 - 别为了省事把所有执行体都包一层
json.Marshal——这增加序列化开销,且掩盖原始错误类型
OnStateChange 日志里看不到 HalfOpen → Closed 切换
这个现象很常见,根本原因是:gobreaker 只在**状态真正变更时**调用 OnStateChange,而 HalfOpen → Closed 的判定发生在试探请求成功之后,并且要满足“未触发新的 ReadyToTrip”条件。它不是独立的状态跳转,而是试探成功 + 计数清零后的自然回落。
换句话说:没有专门的 “Closed” 事件,只有 HalfOpen → Open(失败)或 HalfOpen → Closed(成功且未再失败)两种路径,后者不会单独触发回调。
- 想确认是否恢复成功?在
cb.Execute成功返回后加日志,比依赖OnStateChange更可靠 -
OnStateChange主要用于告警和可观测性,不适合做恢复逻辑分支依据 - 如果你发现一直卡在
HalfOpen,大概率是试探请求本身没发出去,或者发出去但被超时/网络拦截了——检查Timeout是否比下游真实超时还短
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











