必须用 Gauge 记录在线人数等瞬时值,因其支持增减和设值、线程安全;需通过 prometheus.Register 注册并暴露 /metrics 端点,由业务逻辑驱动更新(如连接建立/断开),避免轮询或未注册。

用 prometheus.NewGauge 创建瞬时值指标
在线人数这类“此刻是多少”的值,必须用 Gauge,不能用 Counter 或 Histogram。它支持增、减、直接设值,底层是原子浮点数,线程安全。
常见错误是直接 new 一个 struct 或用普通变量存数再暴露——Prometheus 客户端不会自动采集这种变量;必须通过官方注册器(prometheus.Register)注册的 Gauge 实例才有效。
-
Gauge的零值是 0,初始化后默认就可被采集,无需手动触发首次上报 - 推荐用
prometheus.NewGauge(prometheus.GaugeOpts{...})构造,别用prometheus.NewGaugeVec除非你要按 label 区分维度(比如按服务实例) - 如果指标名含下划线(如
online_users),没问题;但避免用空格、点号、斜杠
在 HTTP handler 中更新 Gauge.Set 值
在线人数本质是运行时状态,得靠业务逻辑驱动更新:用户登录/登出、心跳续期、连接断开时调用 .Set()。不要轮询 DB 或 Redis 查总数——延迟高、压力大、不准。
典型场景是 WebSocket 连接管理或长连接网关:每个连接建立时 onlineUsersGauge.Inc(),断开时 onlineUsersGauge.Dec();或者更稳妥地,在连接池里维护计数器,用 .Set(float64(n)) 直接写入当前值。
立即学习“go语言免费学习笔记(深入)”;
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 用
.Inc()/.Dec()简单,但若中间有 panic 或连接未正常关闭,计数会漂移;生产环境建议配合 defer 或连接生命周期钩子做兜底 - 避免在 HTTP handler 里频繁调用
.Set()(比如每秒几十次),Gauge 本身无性能瓶颈,但高频写可能掩盖真实业务节奏 - 如果使用 Redis 存在线列表,建议只在关键节点(如登录成功、token 过期回调)同步更新 Gauge,而非每次请求都查
注册并暴露给 Prometheus 抓取
不注册 = 不可见。必须把 Gauge 实例传给 prometheus.MustRegister 或自定义 prometheus.Registry,再挂到 HTTP 路由上。
错误做法:只创建 Gauge,没注册;或注册了但没启动 /metrics 端点;或用了 http.DefaultServeMux 却被其他框架(如 Gin、Echo)覆盖路由。
- 标准写法:
http.Handle("/metrics", promhttp.Handler()),且确保该路由在http.ListenAndServe前注册 - 如果用 Gin,得用
gin.WrapH(promhttp.Handler()),不是直接c.String拼指标文本 - 检查
curl http://localhost:8080/metrics | grep online_users是否返回类似online_users 127的行;没有则一定是注册或路由环节漏了
注意 label 使用和并发安全边界
如果你要按地区、设备类型等维度拆分在线人数,得用 GaugeVec,但代价是内存占用上升、查询聚合变复杂。单维度 Gauge 更轻量、更易监控告警。
常见坑是多个 goroutine 并发调用同一个 Gauge 的 .Set() ——其实完全 OK,Gauge 内部用 atomic.StoreUint64,无需额外加锁。但如果你自己维护了一个 map 记录各房间人数,再汇总给 Gauge,那个 map 就必须加锁或用 sync.Map。
-
GaugeVec的WithLabelValues返回的是临时指标句柄,重复调用相同 label 会复用,但 label 值不能为"",否则 panic - 上线后观察 Prometheus 查询
online_users是否有突刺或负值:出现负值说明 Dec 多于 Inc,逻辑没对齐连接生命周期 - 如果服务重启,Gauge 自动归零,这是预期行为;别试图持久化它的值——瞬时指标本就不该跨进程保留

















