必须用两个 counter 而非 gauge,因 slo 需滑动窗口内成功率(成功数/总数),prometheus 的 rate() 仅支持单调递增 counter 计算可靠速率;gauge 为瞬时值,无法聚合、回溯且重启归零。

Go 服务暴露的 SLO 指标必须是两个独立的 Counter,不能用 Gauge 记录成功率,否则 Prometheus 无法做滑动窗口聚合、无法回溯、重启后归零。
为什么必须用两个 Counter 而不是单个 Gauge?
SLI(如成功率)本质是“成功请求数 / 总请求数”,而 SLO 要求在滑动时间窗口(如 7d)内计算该比率。Prometheus 的 rate() 函数只能对单调递增的 Counter 做可靠速率估算;Gauge 是瞬时值,无法还原历史趋势,且重启后丢失上下文。
-
http_requests_total{handler="api",status_code=~"2..|3.."}—— 分母,覆盖所有应计入 SLO 的请求(注意:4xx 是否计入取决于你的 SLO 协议,通常不进分母) -
http_requests_slo_ok_total{handler="api"}—— 分子,只在满足 SLO 条件时 +1(例如:耗时 ≤ 300ms 且 状态码为 2xx) - 错误示例:
http_requests_success_rate_gauge{handler="api"} 0.992—— 这类指标在 Prometheus 中无法参与rate()或histogram_quantile()计算,也不支持按标签下钻分析
如何在 Go 中判断单次请求是否满足 SLO 条件?
不能只看延迟,也不能只看状态码——SLO 是组合条件。比如定义为“99% 请求 P99 ≤ 300ms 且状态码为 2xx”,那每次请求结束时必须同时验证两个维度。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 延迟测量起点必须是
http.Handler入口,用time.Now(),不要依赖中间件外的时间戳 - 状态码必须通过包装
http.ResponseWriter(如自定义responseWriter结构体)捕获,不能读http.Error()后就默认失败 - 判断逻辑要写在 defer 或 handler return 前:
if dur - 若 SLO 分级(如核心接口 99.9%,非核心 99%),需用不同标签(如
level="critical")或不同指标名隔离,避免混算
Prometheus histogram bucket 怎么设才不翻车?
bucket 不是为了凑数,而是为了支撑 histogram_quantile() 插值得到准确的 P90/P99。如果你的 SLO 要求 P99 ≤ 300ms,那 300 必须是 bucket 边界之一。
- 推荐 bucket 设置:
[]float64{10, 50, 100, 200, 300, 500, 1000, 2000}—— 覆盖 SLO 阈值(300),前面有细粒度(100/200),后面有冗余(500/1000) - 错误做法:用默认 bucket(如
promauto.NewHistogramVec不传Buckets),或把 300 放在中间(如{100,200,400,800}),会导致插值偏差大,P99 显示虚高或虚低 - 注意:histogram 本身不直接用于 SLO 成功率计算,但它是验证延迟 SLI 是否达标的必要基础;SLO 成功率仍靠两个 Counter +
rate()
最容易被忽略的是:SLO 的分母定义必须和业务协议一致——哪些请求该计入、哪些 4xx 可豁免、是否排除健康检查路径。这些逻辑一旦写死在指标打点里,后续改起来成本极高。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










