Counter适用于只增不减的累计事件(如订单总数),Gauge适用于可增减的瞬时状态(如活跃订单数);指标需预定义label,注册后不可修改,维度设计需谨慎避免爆炸。

Counter 适合记录“只增不减”的业务事件次数
Counter 是 Prometheus 最基础的指标类型,适用于累计型数据,比如接口调用总数、订单创建数、消息发送成功数。它不支持减操作,Inc() 和 Add() 是唯一合法写法。
- 如果你试图用
Sub()或负数Add(-1),Prometheus 客户端会 panic(Go 版本 v1.12+ 默认行为) - 多个 goroutine 并发调用
Inc()是安全的,底层用了原子操作 - 指标名必须符合 Prometheus 命名规范:只能含字母、数字、下划线,且必须以字母或下划线开头,比如
order_created_total(_total后缀是 Counter 的约定)
var orderCreatedCounter = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "order_created_total",
Help: "Total number of orders created",
})
func init() {
prometheus.MustRegister(orderCreatedCounter)
}
// 在业务逻辑中
func createOrder() {
// ... 创建逻辑
orderCreatedCounter.Inc()
}
Gauge 更适合记录“可增可减”的瞬时状态值
Gauge 用于表示当前状态,比如在线用户数、队列长度、内存使用 MB 数。它支持 Inc()、Dec()、Set()、Add(),但要注意:没有自动重置机制,你得自己控制何时设为 0 或其他基准值。
- 不要拿 Gauge 去替代 Counter 记录事件次数——比如用
gauge.Set(1)表示“又来一个请求”,这会导致指标含义模糊、不可聚合 - 如果业务中需要“当前活跃订单数”,用 Gauge 合理;但“历史总订单数”必须用 Counter
-
Set()是最常用方法,尤其在采集周期性快照时(如每秒读取一次 Redis 连接池空闲数)
var activeOrderGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "active_order_count",
Help: "Number of currently active orders",
})
func init() {
prometheus.MustRegister(activeOrderGauge)
}
// 下单时 +1,取消/完成时 -1
func placeOrder() {
activeOrderGauge.Inc()
}
func cancelOrder() {
activeOrderGauge.Dec()
}
带标签(Labels)的指标必须预定义所有 label 名称
Prometheus 要求所有 label key 必须在指标注册前声明,运行时不能动态新增 label 名称(比如从 "env" 突然变成 "region"),否则 prometheus.MustRegister() 会 panic。
- 错误做法:每次调用都 new 一个带不同 label key 的 Counter —— 这会注册多个指标,造成 cardinality 爆炸
- 正确做法:用
prometheus.NewCounterVec预定义 label keys(如[]string{"service", "status"}),再通过WithLabelValues("api", "200")获取子指标实例
var httpReqCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total HTTP requests processed",
},
[]string{"service", "method", "status"},
)
func init() {
prometheus.MustRegister(httpReqCounter)
}
// 使用时
httpReqCounter.WithLabelValues("user-api", "POST", "200").Inc()
- 注意:
WithLabelValues()传参顺序必须和NewCounterVec中[]string定义顺序严格一致,错位不会报错但会导致 label 值错乱(比如 status 显示成 method)
注册后修改指标定义会 panic,热更新需绕过原生注册流程
Prometheus Go client 默认不允许重复注册同名指标,prometheus.MustRegister() 第二次调用相同名称会直接 panic。这意味着:
-
不能在 HTTP handler 里反复调用
NewCounter+MustRegister
Golang Uber Dig下载使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
不能靠“重新注册新指标”来切换 label 结构或 Help 文本
若需运行时动态控制指标开关(比如灰度开启某业务指标),应提前注册好所有可能用到的指标,再用闭包或配置开关控制是否调用
Inc()/Set()更隐蔽的坑:测试中用
prometheus.NewRegistry()替换默认 registry 时,别忘了把自定义指标显式注册到新 registry,否则promhttp.HandlerFor()暴露的 metrics 里看不到它们
指标维度设计一旦上线就很难收缩,label 组合爆炸比性能问题更难收场。上线前用 curl @#@#@#@#@#@#@#@#@#@0 | grep your_metric_name 看一眼实际暴露的样本行,比看代码更能发现 label 是否失控。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










