go服务暴露/metrics端点应使用独立http server或专用servemux,注册指标须在listenandserve前完成,避免中间件污染响应;必须暴露http_requests_total和http_request_duration_seconds(histogramvec),遵循red原则支撑sla监控。

直接用 promhttp.Handler() 暴露 /metrics,Prometheus 抓不到指标,90% 是端点没通、路径不对或响应格式被污染,不是配置写错了。
Go 服务里怎么挂 /metrics 端点才不踩坑
别把 /metrics 和业务路由混在同一个 http.ServeMux 里——中间件(比如日志、CORS、认证)可能篡改响应头或状态码,导致 Prometheus 报错:expected 'text/plain' response, got 'text/html'。
- 用独立的 HTTP server 启一个纯指标端口(比如
:9090),只挂promhttp.Handler() - 如果必须复用主端口,用
http.NewServeMux()单独建 mux,不走全局http.DefaultServeMux - 注册所有指标(
prometheus.MustRegister())必须在http.ListenAndServe()之前完成;否则访问/metrics返回空或 404 - 别在 handler 里动态创建新指标(比如每次请求都
prometheus.NewCounter()),会 panic 并泄漏内存
该暴露哪些指标才算真正支撑 SLA 监控
SLA 关注的是“请求是否成功”和“耗时是否达标”,不是 CPU 或 goroutine 数——那些是资源类指标,辅助排查,不能替代 RED(Rate/Errors/Duration)。
- 必须暴露
http_requests_total:用CounterVec,标签设method、path、status;别拼接字符串做 label 值(如"200_ok"),会制造高基数 - 必须暴露
http_request_duration_seconds:用HistogramVec,Buckets要覆盖你的 SLA 目标(比如 SLA 是 200ms,至少含0.05、0.1、0.2、0.5) - 别用
Summary替代Histogram:它不支持rate(),P99 计算不准;也别把status当Histogram的 label,直方图不支持多维分桶 - 避免高频打点失败:在中间件里调
histogram.Observe()前加defer,确保 panic 时仍能记录耗时
Prometheus 配置里怎么写 job 才能抓到数据
关键不是 job_name 写得多漂亮,而是 target 是否真实可达、路径是否返回合法文本、Content-Type 是否为 text/plain; version=0.0.4。
- 先手动
curl -v http://your-service:8080/metrics,确认返回 200 + 正确 MIME 类型 + 非空指标文本 -
scrape_configs中的targets必须是服务实际监听的地址(K8s 环境注意 Service DNS 名、Pod IP 或 Headless Service) - 别漏掉
metric_relabel_configs:如果 Go 服务用了promauto,默认指标名带go_前缀,不影响;但自定义指标命名要符合 Prometheus 规范(小写字母、下划线) - 确保
_bucket指标全量采集:Histogram 自动产生xxx_bucket、xxx_sum、xxx_count,三者缺一不可,否则rate()和分位数计算失效
为什么用 Prometheus 拉取而不是自己轮询打点
拉取模式天然规避了客户端失联、时间窗口错位、网络抖动丢点等问题;自己轮询 HTTP 接口上报,等于把监控系统的可靠性绑死在服务自身的可用性上。
- Go 原生支持
expvar和promhttp,开箱即用;promhttp.Handler()自动处理序列化、缓存头、Content-Type,别手写指标输出 - 如果已有 Gin/Echo 等框架,用
gin.WrapH(promhttp.Handler()),别用c.Data()或c.String()手动写响应体 - Alertmanager 告警规则必须配合
rate()使用(比如rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01),并加守卫条件(如count by (job) (up == 0) == 0)防误报
最常被忽略的其实是指标生命周期管理:注册一次就够了,复用已注册的 CounterVec 实例;标签值必须可控(比如 path 别用原始 URL,要归一化为 /user/{id}),否则基数爆炸会导致 Prometheus OOM。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











