kratos官方推荐使用pkg/net/netutil/breaker,它深度集成各层、状态原子安全且自动联动metrics与tracing;禁用sony/gobreaker或手写实现,避免冲突导致指标丢失或半开失效。

kratos pkg/net/netutil/breaker 是唯一推荐的熔断器实现
别用 sony/gobreaker 或自己手写状态机——Kratos 官方维护的 breaker 包已深度集成到 transport、client、middleware 各层,且默认启用。它不依赖外部组件,状态变更原子安全,且与 metrics、tracing 自动联动。
常见错误是引入第三方熔断库后,和 Kratos 的 transport/http.Client 或 transport/grpc.Client 冲突,导致 Allow() 调用被绕过、指标丢失或半开探测失效。
-
breaker实例必须按下游服务粒度创建,例如调用支付服务用一个,调用用户服务用另一个,命名建议带服务标识:"payment-service-breaker" - 不要全局复用同一个 breaker 实例,否则 A 服务故障会误触发 B 服务熔断
- 它不提供
fallback参数,降级逻辑需在breaker.Allow()失败后由业务层显式处理
breaker.Allow() 必须包裹在实际请求发起前一刻
不是包住整个 client 初始化,也不是包住整个 handler 函数,而是紧贴网络调用前调用。Kratos 的 HTTP/gRPC client 默认不自动集成 breaker,你得手动加。
典型错误写法:breaker.Allow() 放在 http.NewRequest() 之前但没做 error 判断,或者放在 client.Do(req) 之后——这会让失败统计完全失真。
- 正确位置:在构建好 request、context 已带 timeout 后,立即调用
breaker.Allow() - 若返回非 nil error(如
breaker.ErrNotAllowed),应直接返回降级响应,不再执行client.Do(req) - 若调用成功,后续必须配对调用
breaker.MarkSuccess()或breaker.MarkFailed(),不能只靠 defer
MarkSuccess / MarkFailed 的触发条件必须精准匹配业务语义
Kratos 的 breaker 不自动解析 HTTP 状态码或 gRPC 错误码,它把“是否失败”的判断权完全交给业务代码。这意味着你得自己定义什么算失败。
最容易被忽略的是:5xx 响应、gRPC codes.Unavailable、codes.Internal 必须主动转为 error 并传给 MarkFailed();而 404、400 这类客户端错误不应计入失败统计,否则成功率会虚低,导致误熔断。
- HTTP 场景下,
resp.StatusCode >= 500时应调用breaker.MarkFailed() - gRPC 场景下,用
status.Code(err)判断,仅当是codes.Unavailable、codes.Internal、codes.DeadlineExceeded才标记失败 - 成功路径上必须调用
breaker.MarkSuccess(),哪怕 response body 解析失败也不影响——那是业务逻辑层的事
熔断状态观测必须通过 Prometheus 指标而非日志
Kratos 的 breaker 默认暴露 circuit_breaker_state{service="xxx"} 和 circuit_breaker_requests_total{state="open|closed|halfopen"},但很多人只看日志里 “breaker open” 就以为稳了,其实根本不知道半开探测是否真正发起、失败率是否持续超标。
真实线上问题往往出现在:Interval 设置太短(如 10s),把瞬时抖动当持续故障;或 MaxRequests 设为 1,导致单次超时就熔断,恢复又极快,形成高频震荡。
关键配置建议:
-
Interval:设为 60s~120s,避免滑动窗口重置过频 -
MaxRequests:≥10,确保统计基数可靠 -
RequestCountAfterOpen(半开请求数):保持默认 1 即可,够用 - 务必在 Grafana 中建面板盯住
circuit_breaker_state和circuit_breaker_requests_total的比率变化,比任何日志都准
复杂点在于:Kratos 的 breaker 没有公开的 “当前是否处于半开” 状态查询接口,你只能靠指标推断——如果 circuit_breaker_state 长时间卡在 open,但 circuit_breaker_requests_total{state="halfopen"} 有计数且失败,说明下游确实没恢复;如果 halfopen 计数为 0,大概率是 Interval 还没到,或 breaker 根本没被触发过 Allow。











