runtime.NumGoroutine() 不能直接画进 Grafana,因为它只是纯内存整数值,无时间戳、非 Prometheus 指标;必须通过 prometheus.Gauge 封装并由 Prometheus 抓取后,Grafana 才能查询时序数据。

为什么 runtime.NumGoroutine() 不能直接画进 Grafana
因为 runtime.NumGoroutine() 是个纯内存值,不带时间戳、无类型、不暴露为 Prometheus 指标——Grafana 只能查 Prometheus 存储的时序数据,不是 Go 运行时变量。你直接调用它返回一个 int,Grafana 根本看不到。
必须通过 prometheus.Gauge 把它“转成指标”,再由 Prometheus 抓取、存储、提供查询接口。
- 别在 handler 里每请求调一次
runtime.NumGoroutine()并.Set()—— 高频写入会拖慢 HTTP 响应,且样本过于密集无聚合意义 - 用定时 goroutine(如每 5 秒)读取并更新
Gauge,平衡实时性与开销 - 确保该
Gauge已注册到默认 registry 或你显式传入的 registry,否则/metrics不会输出
如何暴露 goroutine 和内存指标给 Prometheus
Go 客户端库自带运行时指标,但默认只注册部分(如 go_goroutines),而内存指标(如堆大小、GC 统计)需手动启用 prometheus.NewGoCollector()。
示例代码片段:
立即学习“go语言免费学习笔记(深入)”;
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"net/http"
)
func init() {
// 注册标准 Go 运行时指标(含 goroutine 数、内存分配、GC 次数等)
prometheus.MustRegister(prometheus.NewGoCollector())
// 如果你还想加自定义 gauge(比如监控某池子协程数),单独 NewGauge 并 Register
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":9090", nil)
}
-
go_goroutines对应runtime.NumGoroutine(),单位:个 - 关键内存指标包括:
go_memstats_alloc_bytes(当前已分配堆内存)、go_memstats_heap_objects(堆对象数)、go_memstats_gc_cpu_fraction(GC 占用 CPU 比例) - 不要漏掉
prometheus.NewGoCollector()—— 默认promhttp.Handler()不自动包含完整运行时指标
Grafana 查询时 goroutine 图表为什么跳变剧烈或显示 NaN
直接画 go_goroutines 原始值容易受瞬时调度抖动影响(比如 GC STW 瞬间协程挂起),导致图表毛刺严重;更糟的是,若 Prometheus 抓取失败或样本缺失,rate() 类函数会返回 NaN,而 go_goroutines 是 Gauge,根本不能套 rate()。
- 用
avg_over_time(go_goroutines[1m])替代裸值,平滑短时波动 - 避免写
rate(go_goroutines[1m])——rate()只适用于单调递增的Counter,对Gauge无意义,结果必为 0 或 NaN - 设置 alert rule 时,用
go_goroutines > 5000而非rate(...),并搭配for: 2m避免误报 - 确认 Prometheus 的
scrape_interval≤ 30s,否则 1m range vector 可能拿不到足够样本
内存指标选哪个才反映真实压力
go_memstats_alloc_bytes 最常用,但它只反映“当前已分配且未释放”的堆内存,不包括 OS 未回收的页、栈内存、CGO 分配内存。真正预警应关注趋势+突变,而非绝对值。
- 优先看
go_memstats_alloc_bytes的 5m 移动平均:avg_over_time(go_memstats_alloc_bytes[5m]) - 配合
go_memstats_heap_objects判断是否对象泄漏(持续上升不回落) -
go_memstats_gc_cpu_fraction> 0.3 持续 1min,说明 GC 开始拖慢业务,需介入 - 别依赖
process_resident_memory_bytes—— 它含缓存、mmap 映射等噪声,对 Go 应用诊断价值低
goroutine 和内存监控不是“开了就行”,而是要理解每个指标的采集机制、语义边界和 Prometheus 查询行为。漏掉 NewGoCollector()、误用 rate()、盯着裸值调阈值——这些都会让图表看起来热闹,却失去告警有效性。



















