expvar 可轻量暴露 go runtime 指标:默认提供 goroutines、memstats、gc 次数等,注册 http.handlefunc("/debug/vars", expvar.handler) 即可返回 json;需注意字段命名、前端差值计算、安全控制及采样语义。

用 expvar 暴露基础 Runtime 指标最轻量
Go 标准库的 expvar 包默认收集 goroutines、memstats、gc 次数等关键指标,不依赖第三方,也不需改业务逻辑。直接导入后注册 HTTP handler 就能被前端轮询。
- 必须在
main()里调用http.ListenAndServe前注册,否则路由无效:http.HandleFunc("/debug/vars", expvar.Handler) - 返回是 JSON 格式,但字段名是 Go 风格(如
MemStats.Alloc),前端解析时注意嵌套层级 - 不要在生产环境长期开启
/debug/pprof,但/debug/vars开销极低,可常驻 - 若用反向代理(如 Nginx),需显式透传
Content-Type: application/json,否则浏览器可能拒解析
前端轮询 /debug/vars 时避免阻塞主线程
直接用 fetch 轮询没问题,但每秒一次高频请求若用 async/await 链式调用,容易因某次超时或失败导致后续请求全部堆积。
- 用
setTimeout+ 回调方式发起下一轮,而非setInterval:防止请求叠加 - 加
signal控制超时,例如AbortController配合fetch(..., { signal }) - 对
MemStats.TotalAlloc这类单调递增指标,前端应做差值计算(如每秒分配量),而不是直接渲染原始值 - 首次加载时先取一次快照,再启动轮询,避免图表初始为空白
想看更细粒度指标?得手动注册 expvar.NewInt 或 expvar.NewFloat
标准 expvar 不暴露 GC 暂停时间、P 数量、当前 GOMAXPROCS 值等。这些需要自己采集并注册变量。
- 在
init()或main()里定义:var gcPause = expvar.NewFloat("gc/pause_ms") - 用
runtime.ReadGCStats获取PauseNs切片,取最新值转毫秒后调用gcPause.Set(float64(ms)) - 注意并发安全:所有
expvar类型的Set方法是线程安全的,但读取PauseNs本身要加锁或用局部变量暂存 - 别把
runtime.MemStats全量塞进expvar.Map—— 字段太多会拖慢序列化,只挑关键字段同步
上线前必须关掉调试端点或加访问控制
/debug/vars 泄露的信息比看起来多:比如 cmdline 可能含启动参数,http.Server 相关变量可能暴露内部路由结构。
立即学习“go语言免费学习笔记(深入)”;
- 用构建 tag 控制是否注册:
//go:build !prod,上线编译加-tags prod - 若必须开放,至少用中间件校验
Authorizationheader 或 IP 白名单,不要依赖前端隐藏路径 - Nginx 层可配置
location /debug { deny all; },比代码层更可靠 - 某些容器平台(如 Kubernetes)会自动抓取
/metrics,别让/debug/vars和 Prometheus 的/metrics端口冲突
真正麻烦的不是怎么拿到数字,而是指标含义和采样节奏是否匹配业务场景——比如用 goroutines 数判断泄漏,得连续观察趋势,单次数值毫无意义;而 MemStats.PauseTotalNs 是累计值,前端必须自己做 delta 计算。这些细节不写进代码注释,过两个月连你自己都得重读 runtime 文档。










