RPC 延迟飙升别再靠猜了:go-zero 监控+Prometheus 3 个指标搞定
【免费下载链接】go-zeroA cloud-native Go microservices framework with cli tool for productivity.项目地址: https://gitcode.com/GitHub_Trending/go/go-zero
周三下午 3 点,订单接口 P99 从 80ms 跳到 2.3s,Grafana 上 CPU、内存全绿,没人说得清是哪个下游拖的。翻了 40 分钟日志才定位到某个方法一次慢查询,最后发现不是"哪台机器的问题",是"哪个方法的问题"。当时最缺的不是硬件监控,是方法级的耗时数据。
go-zero 的监控恰好补的就是这块:配置里加两个开关,RPC 方法级的延迟直方图、错误码计数自动产出。这篇讲怎么把 go-zero 的 Prometheus 指标从 0 拉起来,10 分钟能跑通,全程不用改业务代码。
你能带走什么
- 3 行配置让
/metrics端口吐出来,curl 即见 12 个桶的延迟直方图 - 3 行 YAML 配好 Prometheus 采集 go-zero 服务,不用自写 exporter
- 2 条 PromQL 直接算出 P99 延迟和错误率,不是抄指标名
- 2 条告警规则模板(错误率 + P99),改个阈值就能上生产
🔧 选型逻辑:为什么是 Prometheus 这套
监控选型的核心取舍不是功能多少,而是采集成本:go-zero 的指标出口是寄生在业务进程里的,没有独立 collector、不引入额外协议,延迟敏感的服务零负担。这也是它比自建 APM 轻的地方。
- 配置结构里预留了标准 Prometheus 出口,core/service/serviceconf.go 里服务启动时自动拉起指标 HTTP 端口,默认 9101
- zrpc 内置拦截器 zrpc/internal/serverinterceptors/prometheusinterceptor.go 给每个方法自动记耗时和 gRPC 错误码,你一行代码不用写
- 没配指标 Host 时,所有 Observe 操作直接短路(core/metric/metric.go),不开监控时业务路径零开销
📊 Phase 1:让指标端口活过来
交付物:curl localhost:9101/metrics能拉到带rpc_server_前缀的指标行。
在服务配置里加一段:
Prometheus: Host: 0.0.0.0 # 填了 Host 才启用 Port: 9101 # 指标端口,默认即此值启动逻辑在 core/prometheus/agent.go:StartAgent判断 Host 非空后起一个独立 HTTP 服务挂/metrics,和 gRPC 业务端口互不干扰。
验证点:
curl -s localhost:9101/metrics | grep rpc_server启动服务后能看到rpc_server_requests_duration_ms_bucket之类的输出,说明出口通了。此时曲线还没数据——方法级指标要等 Phase 2 的拦截器。
端口通了,数据面还差一个开关。
⚡ Phase 2:打开方法级指标采集
交付物:Prometheus 里出现每个 RPC 方法的延迟桶和 code 计数。
RPC 服务端配置里打开 Middlewares 的 Prometheus 开关:
Middlewares: Prometheus: true # 注册 Prometheus 拦截器注册发生在 zrpc/server.go 的setupUnaryInterceptors,拦截器本体逻辑只有 4 行:
startTime := timex.Now() resp, err := handler(ctx, req) metricServerReqDur.Observe(timex.Since(startTime).Milliseconds(), info.FullMethod) metricServerReqCodeTotal.Inc(info.FullMethod, strconv.Itoa(int(status.Code(err))))两个指标:rpc_server_requests_duration_ms(Histogram,method 标签)和rpc_server_requests_code_total(Counter,method + code 标签)。客户端侧同理,RpcClientConf.Middlewares也支持这个开关,zrpc/internal/clientinterceptors/prometheusinterceptor.go 记的是"我调下游"的耗时。
验证点:发起一次 RPC 调用后,
curl -s localhost:9101/metrics | grep -E "duration_ms_bucket|code_total"到这一步,每个方法的耗时分布和 gRPC 错误码分布都有了,PromQL 想怎么写都行。
采集端还差一个 job。
📈 Phase 3:Prometheus 采集 + 2 条告警
交付物:P99 曲线和错误率曲线出现在 Grafana,告警规则入库。
scrape_configs: - job_name: 'go-zero' static_configs: - targets: ['10.0.0.5:9101'] # 换成你的实例 scrape_interval: 15s两条最常用的查询,直接建面板:
# P99 延迟 histogram_quantile(0.99, sum(rate(rpc_server_requests_duration_ms_bucket[5m])) by (le, method)) # 错误率(code 0 即 gRPC 无错) sum(rate(rpc_server_requests_code_total{code!="0"}[5m])) / sum(rate(rpc_server_requests_code_total[5m]))告警规则模板:
- alert: GoZeroRpcErrorRate expr: | sum(rate(rpc_server_requests_code_total{code!="0"}[5m])) / sum(rate(rpc_server_requests_code_total[5m])) > 0.01 for: 5mP99 那条把表达式里的分位数换成 0.99、阈值按你的业务 SLO 定,for建议 5m,避免单次毛刺刷告警。
🐛 一个设计细节值得展开:延迟桶为什么这么切
拦截器里那 12 个桶不是随手写的:
Buckets: []float64{1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2000, 5000}core/metric/histogram.go 里NewHistogramVec把这组桶透传给 client_golang,prom.MustRegister注册时还会做冲突检查——两个同名指标重复注册会直接 panic,而不是悄悄覆盖,这其实是帮你兜底配置错误的设计。
桶的逻辑:le是"小于等于"语义,P99 落在哪个桶,分辨率就是相邻两个桶之间的差值。1ms~10ms 是缓存命中的常见区间,切得密;500ms 以后单次请求已经算事故级,再细分没意义,所以 2000 直接跳 5000。如果你的业务 P99 稳定在 20ms 以内,这组桶在 10~25 这一段会浪费精度——Buckets字段是开放的,fork 改桶分布是合法的调优手段。
🐛 我踩过的坑
🐛端口开着,但只有 runtime 指标→ 原因:没开Middlewares.Prometheus。StartAgent只起端口,方法级指标全靠拦截器,开关在 RPC 配置里,不在 ServiceConf 里。解法:Phase 2 那行开关补上,重启。
🐛curl 9101 拒绝连接,日志没报错→ 原因:StartAgent对 Host 为空直接 return,监听失败也只在日志里记一条。解法:ss -lntp | grep 9101确认端口归属,再回配置确认 Host 没拼错。
🐛Prometheus 侧内存涨得快→ 原因:标签基数问题。method标签基数 = 方法数,几十个没问题;但如果哪天有人往code位置塞了业务自定义串(比如把错误信息塞进 code),基数就爆了。解法:Grafana 里对rpc_server_requests_code_total的 code 值做一次count by (code)巡检,采集间隔保持 15s 起步,别用 1s。
效果:Before vs After
以下为示例数据,实际因业务而异
| 指标 | 接入前 | 接入后 | 变化幅度 |
|---|---|---|---|
| 单次慢查询定位耗时 | 40min(翻日志+重启复现) | 5min 内(P99 曲线 + code 分布) | 约 8 倍 |
| 慢方法发现时机 | 用户反馈后 | 告警先于用户 | 由被动转主动 |
| P99 回归确认 | 人工压测对比 | 一条 PromQL | 由天级到分钟级 |
| 新增监控点成本 | 每接口手写埋点 | 零代码,开关默认存在 | 从代码级到配置级 |
核心变化一句话:定位问题从"猜哪台机器"变成"查哪个 method"。
下一步
- 接上调用链—— zrpc 的
Telemetry配置位已就位,指标和 trace 用同一套 id 关联 - 补下游视角—— 客户端侧拦截器已内置,"我慢还是下游慢"一眼分出来
- 持续剖析——
ServiceConf里的Profiling配置接 Pyroscope,不用等 OOM 才开 pprof
最小行动:今晚把 Phase 1 和 Phase 2 那两段配置加到 staging 环境,明天就能看到第一条方法级 P99 曲线。
【免费下载链接】go-zeroA cloud-native Go microservices framework with cli tool for productivity.项目地址: https://gitcode.com/GitHub_Trending/go/go-zero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考