expvar.newint和newfloat未生效是因为创建后未注册到全局expvar.var表;需显式调用expvar.publish或使用expvar.register传指针注册,否则curl /debug/vars看不到数据。

expvar.NewInt 和 expvar.NewFloat 为什么没效果?
因为 expvar 的变量必须注册到默认的 expvar.Publish(即全局 expvar.Var 注册表)才可通过 HTTP 暴露,而 NewInt/NewFloat 返回的是未注册的裸变量。直接调用它们只是创建了对象,不自动挂载。
- 正确做法是:先用
expvar.NewInt("counter")创建,再用expvar.Publish("counter", counterVar)显式注册(虽然更常用的是直接用expvar.Get("counter")获取已注册项,或改用expvar.Do遍历) - 更省事的方式是跳过 NewXXX,直接用
expvar.Register("name", &intVar)—— 注意传指针,且该变量需支持expvar.Var接口(Int和Float类型本身已实现) - 常见错误现象:
curl http://localhost:6060/debug/vars返回空 JSON 或找不到自定义字段,基本就是忘了注册或注册名拼错
如何让 expvar 在非标准端口暴露?
expvar 本身不启动 HTTP 服务,它只提供数据源;真正暴露靠你手动注册 handler 到某个 http.ServeMux。默认 http.DefaultServeMux 上的 /debug/vars 路径,仅在你显式调用 http.ListenAndServe 且没传自定义 mux 时才“看起来”是自动的。
- 若要用 8081 端口:启动时写
http.ListenAndServe(":8081", nil),然后确保没其他 mux 覆盖/debug/vars - 若已有自定义 mux(比如用 gorilla/mux 或 http.ServeMux 实例),必须手动添加:
mux.HandleFunc("/debug/vars", expvar.Handler().ServeHTTP) - 注意路径前缀:如果 mux 设置了
/api前缀,别把/debug/vars错挂成/api/debug/vars——expvar.Handler()只响应精确匹配的/debug/vars
自定义结构体指标导出后显示为 {} 怎么办?
expvar 对 struct 的序列化依赖其字段是否可导出(首字母大写)+ 是否实现了 expvar.Var 接口或 json.Marshaler。裸 struct 默认被 json 包处理,但 expvar.Handler 内部用的是自己的编码逻辑,对未注册、无 Marshal 方法、字段不可导出的 struct 会输出空对象 {}。
- 最简解法:让结构体字段全部大写,并嵌入
expvar.Map或直接组合expvar.Int/expvar.String字段,例如:type Stats struct { Total *expvar.Int; Errors *expvar.Int } - 避免用
map[string]interface{}存指标——类型擦除后expvar无法识别子值,容易变成{"key":{}} - 调试技巧:在 handler 中加一行
log.Printf("stats: %+v", stats),确认结构体字段值确实存在且非 nil
expvar 和 pprof 能共用同一端口吗?
能,而且推荐这么做。两者都基于 http.DefaultServeMux,只要不冲突路径即可共存:/debug/vars(expvar)、/debug/pprof/(pprof)互不干扰。
立即学习“go语言免费学习笔记(深入)”;
- 标准做法是导入
_ "net/http/pprof",它会自动注册所有/debug/pprof/*路由到默认 mux;再确保expvar的 handler 也注册在同一 mux(即不传自定义 mux 给http.ListenAndServe) - 常见坑:自己 new 了一个
http.ServeMux并传给ListenAndServe,却忘了把 pprof 和 expvar handler 都加进去,结果只看到其中一个生效 - 性能影响极小:两者都是只读快照,无锁遍历,
expvar.Do遍历所有注册变量的开销与变量数线性相关,百级别变量几乎无感
expvar 最容易被忽略的一点:它不采集任何指标,只提供注册和导出机制。计数器增减、延迟统计、内存快照……全得你自己在业务代码里调 myCounter.Add(1) 或 myGauge.Set(float64(time.Since(start).Microseconds()))。没人替你埋点,也没自动采样周期。










