Go expvar.Func 怎么发布只读的动态指标
如果指标不是“写入时累加”,而是希望每次查看时根据当前状态重新计算,expvar.Func 很合适。它的类型就是 func() any:调用方读取指标时执行回调,再把返回值按 JSON 形式输出。这样可以发布队列长度、当前缓存项数、配置快照等只读动态值,而不用额外维护一份同步变量。
官方资料:https://pkg.go.dev/expvar#Func
最小做法是让回调只做一次快速、并发安全的读取,再用 expvar.Publish("名称", expvar.Func(回调)) 注册。它适合“读时计算”,不适合把慢查询、网络请求或带副作用的任务塞进指标端点。
先判断:这个值是不是应该在读取时计算
expvar.Int、expvar.Float 和 expvar.Map 适合业务代码主动更新的累计量,例如请求总数。expvar.Func 则不保存一个可写的当前值,它每次被读取都会调用函数。两者的接口决策可以这样记:
| 需求 | 更合适的类型 | 原因 |
|---|---|---|
| 请求完成时累加一次 | expvar.Int | 写入路径明确,读取成本低 |
| 查看时读取当前状态 | expvar.Func | 不需要额外同步一份快照 |
| 按键维护多组计数 | expvar.Map | 每个键可以独立更新 |

回调只返回 JSON 能表达的当前状态
官方定义中,Func 通过调用函数并使用 JSON 格式化返回值来实现 Var。因此回调最好返回数字、字符串、布尔值或由这些值组成的结构体/映射,并把共享数据的读取动作控制在一个很小的范围内。
package main
import (
"expvar"
"net/http"
"sync/atomic"
)
var inFlight atomic.Int64
func main() {
// 回调在读取指标时执行,不在这里缓存一个固定结果。
expvar.Publish("in_flight_requests", expvar.Func(func() any {
// atomic.Load 保证并发读取不会产生数据竞争。
return inFlight.Load()
}))
// 使用标准库的调试端点输出所有已注册变量。
http.ListenAndServe(":8080", nil)
}
如果需要返回多个字段,可以让回调返回一个结构体。字段名会成为 JSON 键,但不要把内部对象、指针或未受控的字符串直接暴露到调试端点;指标接口同样需要遵守最小暴露原则。
type queueSnapshot struct {
Depth int64 `json:"depth"`
State string `json:"state"`
}
var queueDepth atomic.Int64
func currentQueue() any {
// 同一时刻读取相关字段,避免返回带有明显矛盾的快照。
depth := queueDepth.Load()
state := "ready"
if depth > 1000 {
state = "backlog"
}
return queueSnapshot{Depth: depth, State: state}
}
注册方式决定指标出现在哪里
注册后,默认的 expvar 包会把变量提供给 /debug/vars。新式 net/http 路由可以直接挂载包返回的 Handler;如果应用已经有自己的 mux,也可以把它安装到指定路径。
func registerMetrics() {
// 名称必须在全局 expvar 表中唯一,重复注册会触发 panic。
expvar.Publish("queue_snapshot", expvar.Func(currentQueue))
}
func main() {
registerMetrics()
mux := http.NewServeMux()
// 将 expvar 的 JSON Handler 放到自定义调试路径。
mux.Handle("GET /internal/vars", expvar.Handler())
http.ListenAndServe(":8080", mux)
}
如果只使用标准默认 mux,可以直接导入 expvar 并使用默认的 /debug/vars。无论哪种方式,调试端点都应放在受控网络或鉴权边界之后;expvar.Func 只负责变量格式化,不负责访问控制。

并发与性能边界要提前写进回调
每次读取都会执行回调,所以回调里不应做数据库全表扫描、远程请求、磁盘遍历或修改业务状态。调试端点被监控系统频繁抓取时,这些操作会把“查看指标”变成新的负载来源。
共享状态需要使用原子类型、互斥锁或不可变快照。若一次读取需要组合多个字段,可以在业务侧定期构造一份快照,回调只返回这份快照;不要在回调里临时拼接一个需要长时间持锁的对象。回调没有错误返回值,复杂采集失败时应提前把失败状态编码为可读字段,而不是把网络错误处理逻辑塞进 Func。
- 回调保持无副作用:只读、快速、可重复调用。
- 返回值保持 JSON 友好,避免不可编码的函数、通道或循环引用。
- 指标名全局唯一,注册动作集中管理,避免初始化顺序难以排查。
- 把端点放在内部网络或认证后的 mux 下,不能把 expvar 当作权限控制。
常见问题
expvar.Func 会自动缓存上一次结果吗?
不会。它的语义是读取时调用函数,因此回调必须能承受读取频率;需要缓存时应由业务侧明确维护快照。
为什么不用 expvar.Int 保存这个值?
如果值来自业务事件的累加,Int 更直接;如果值是读取瞬间的派生状态,Func 可以避免重复维护一个可能过期的副本。
expvar.Func 能替代 Prometheus 指标吗?
本文只讨论标准库 expvar 的 JSON 暴露方式。是否采用 Prometheus 等独立指标系统,要根据采集协议、标签维度、聚合和告警需求另行决策。
Go os.ReadDir 的 DirEntry.Type 为什么可能不完整
- 上一篇
- Go os.ReadDir 的 DirEntry.Type 为什么可能不完整
- 下一篇
- Go go doc 怎么只查看指定符号的文档和源码位置
-
- Golang · Go教程 | 6分钟前 | 标准库 · 错误处理 · go · IO · Go 流式处理 io.Reader io.Pipe CloseWithError PipeWriter PipeReader
- Go io.Pipe 怎么把生产端错误传给读取端
- 213浏览 收藏
-
- Golang · Go教程 | 7分钟前 |
- Go io.OffsetWriter 怎么把输出限制在文件的指定起点
- 129浏览 收藏
-
- Golang · Go教程 | 11分钟前 |
- Go flag.TextVar 怎么把命令行参数解析成自定义配置类型
- 349浏览 收藏
-
- Golang · Go教程 | 16分钟前 |
- Go time.Time 比较结果不一致时怎么去掉单调时钟信息
- 256浏览 收藏
-
- Golang · Go教程 | 19分钟前 |
- Go iter.Pull2 怎么区分迭代结束和迭代器返回的错误值
- 259浏览 收藏
-
- Golang · Go教程 | 19分钟前 | go ·
- Go slices.Chunk 最后一组不足指定大小时怎么处理
- 387浏览 收藏
-
- Golang · Go教程 | 22分钟前 | 数据结构 · 标准库 · go · Go 双向链表 container/list Element InsertBefore
- Go container/list.InsertBefore 为什么传入别的链表元素会失败
- 278浏览 收藏
-
- Golang · Go教程 | 24分钟前 |
- Go iter.Pull 提前停止遍历时为什么要调用 stop
- 390浏览 收藏
-
- Golang · Go教程 | 26分钟前 |
- Go database/sql.DB.Stats 怎么看连接池是否出现等待
- 238浏览 收藏
-
- Golang · Go教程 | 30分钟前 |
- Go database/sql.ColumnTypes 怎么导出查询结果的列名和可空信息
- 191浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 890次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 891次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 974次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 960次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 784次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- GScript 编写标准库示例详解
- 2022-12-30 369浏览
-
- 关于Golang标准库flag的全面讲解
- 2023-02-25 344浏览
-
- Golang标准库unsafe源码解读
- 2022-12-29 464浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览

