tRPC-Go 故障熔断分析
trpc-naming-polaris/v3 熔断机制分析与 HTTP 5xx 熔断接入
适用版本:
trpc-go v0.19.3·trpc-naming-polaris/v3 v3.0.6·polaris-go/v2 v2.6.10适用项目:ad_auth(其他 trpc-go 项目同理,注意核对版本)
TL;DR
- 会默认注入熔断:只要 import 了
trpc-naming-polaris/v3并配置了plugins.selector.polaris(未设default: false),北极星熔断器就会被注册为 trpc-go 的全局默认熔断器,且 polaris SDK 熔断默认enable=true。 - 默认只对 3 类框架错误熔断:
101 客户端超时、111 连接失败、141 网络错误。业务错误(含 HTTP 4xx/5xx)默认按成功上报,不会触发熔断。 - HTTP 503 要熔断:需 ① 让错误上报给 selector(
report_any_err_to_selector或WithShouldErrReportToSelector);② 若走「按名寻址」还需ShouldCircuitBreak判定为失败;③ 下游必须是北极星寻址(ip://不生效)。
一、默认是否注入熔断
1.1 线上配置
|
|
未配置 circuitbreaker、default、report_timeout,全部走默认值。
1.2 插件层:Setup 时无条件注册
naming.go 的 setupComponents 不做任何开关判断,直接调用 circuitbreaker.Setup:
|
|
setDefault 由 default 配置决定,未配置时为 true:
|
|
trpc-go 原默认值是 NoopCircuitBreaker(不熔断),此处被替换为北极星实现。
1.3 SDK 层:polaris-go 默认参数
插件 setSdkCircuitBreaker 只在 yaml 显式配置时覆盖,否则使用 SDK 默认值:
| 项 | 默认值 | 来源 |
|---|---|---|
Enable |
true |
config.DefaultCircuitBreakerEnabled |
| Chain | errorCount → errorRate → errorCheck |
CircuitBreakerConfigImpl.SetDefault |
| errorCount | 连续 10 次失败熔断,统计窗口 1min | errorcount/config.go |
| errorRate | 60s 窗口,错误率 ≥50% 且请求数 ≥10 熔断 | errorrate/config.go |
| sleepWindow | 30s 后进入半开 | DefaultSleepWindow |
| checkPeriod | 2s | DefaultCircuitBreakerCheckPeriod |
| outlierDetection(主动探测) | 关闭;开启时插件强制仅用 tcp 探测 |
DefaultOutlierDetectEnabled / naming.go |
1.4 两条调用路径都生效
| 下游 client 配置方式 | Selector | 上报逻辑 | 摘除熔断实例 |
|---|---|---|---|
target: polaris://xxx |
插件 selector.Selector |
circuitbreaker.Report(包级函数) |
GetOneInstance 过滤 |
仅 name(默认北极星寻址) |
trpc-go TrpcSelector |
DefaultCircuitBreaker.Report(即北极星 CB) |
北极星路由/负载均衡过滤 |
target: ip://... |
ipSelector |
noopCircuitBreak |
不熔断 |
1.5 哪些错误会被计为熔断失败
trpc-go/client/client.go 的 selectorFilter:
|
|
- 仅
RetClientConnectFail(111)、RetClientTimeout(101)、RetClientNetErr(141)的框架错误原样上报。 - 101 超时若耗时
< report_timeout(默认 1ms)会被忽略。 - 业务错误、下游返回的错误码 → 按成功上报。
效果:某下游实例在 60s 内超时/连接失败率 >50%(≥10 请求)或连续失败 10 次,被摘除 30s,之后半开探测恢复。
二、HTTP 503 如何支持熔断
2.1 现象
GetAmsAD fail! err=type:business, code:503, msg:http client codec StatusCode: Service Unavailable
2.2 为什么不生效
-
trpc-go HTTP 客户端对
StatusCode >= 300生成的是业务错误(type:business, code:503):1 2 3 4 5// trpc-go@v0.19.3/http/codec.go if rsp.StatusCode >= http.StatusMultipleChoices { msg.WithClientRspErr(errs.New(rsp.StatusCode, fmt.Sprintf("http client codec StatusCode: %s, body: %q", ...))) } -
selectorFilter中as(err)只认框架错误,shouldErrReportToSelector默认false→ 503 被按成功上报。 -
即使上报了,两条路径的判定也不同:
trpc.http.ams.ads 寻址方式 |
判定逻辑 | 默认是否把 503 计为失败 |
|---|---|---|
target: polaris://xxx |
包级 circuitbreaker.Report → canIgnoreError 只忽略 nil 与短耗时 101 |
是(上报即失败) |
仅 name(TrpcSelector) |
CircuitBreaker.Report → shouldCircuitBreak 默认只认 111/141/101 |
否 |
target: ip://...(当前本地 yaml) |
noopCircuitBreak |
不熔断 |
2.3 改造方案
Step 1(必需):让 503 上报到 selector
方式 A:yaml 配置(零代码)
|
|
副作用:4xx(400/404 等)也会上报,在 polaris target 路径下同样计为失败。
方式 B:代码精准控制(推荐,仅 5xx)
|
|
Step 2:按寻址方式补齐判定
-
target: polaris://:Step 1 即可,上报的错误直接计为失败。 -
仅
name(默认北极星寻址):还需在main.go中、trpc.NewServer()之前注册判定函数(插件在NewServer时 Setup,之后注册无效):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15import pcb "git.code.oa.com/trpc-go/trpc-naming-polaris/v3/circuitbreaker" func main() { pcb.ShouldCircuitBreak(func(err error) pcb.Should { if e, ok := err.(*errs.Error); ok && e.Type == errs.ErrorTypeBusiness { switch e.Code { case 502, 503, 504: return pcb.True } } return pcb.Unknown // 交回默认逻辑(101/111/141) }) s := trpc.NewServer() // ... }ShouldCircuitBreak只影响TrpcSelector路径,对polaris://路径无效。 -
ip://:需改为北极星寻址;或自行selector.NewIPSelectorWithCircuitBreaker重新注册 ip selector(不推荐)。
Step 3(可选):调整熔断参数
|
|
2.4 注意事项
- 北极星熔断是实例级的:仅当下游在北极星有多个实例时才有意义。若是 CLB/VIP 单入口,熔断等于全断;而「全死全活」默认开启,全部熔断时会全部放开,等于没熔断。此类场景更适合调用方级别的降级/限流(如 sentinel / hystrix 类 filter)。
- 503 常代表下游过载/限流:实例级熔断会把流量集中到剩余实例,可能引发雪崩,阈值需保守。
- 业务码不参与熔断:上述方案只对传输层 HTTP 状态码生效;
rsp.Ret != 0等业务码是在proxy.Post返回后判断的,不经过 selector。
附:关键源码位置
| 文件 | 说明 |
|---|---|
trpc-naming-polaris/v3@v3.0.6/naming.go |
setupComponents、setSdkCircuitBreaker、setSdkOutlierDetection |
trpc-naming-polaris/v3@v3.0.6/circuitbreaker/circuitbreaker.go |
Setup、Report、canIgnoreError、ShouldCircuitBreak |
trpc-naming-polaris/v3@v3.0.6/selector/selector.go |
polaris target 路径的 Select / Report |
trpc-go@v0.19.3/client/client.go |
selectorFilter、as() |
trpc-go@v0.19.3/client/config.go |
report_any_err_to_selector |
trpc-go@v0.19.3/naming/selector/trpc_selector.go |
按名寻址路径的熔断上报 |
trpc-go@v0.19.3/naming/selector/ip_selector.go |
ip 直连默认 noopCircuitBreak |
trpc-go@v0.19.3/http/codec.go |
HTTP 状态码转业务错误 |
polaris-go/v2@v2.6.10/pkg/config/default.go、circuitbreaker.go |
SDK 熔断默认参数 |