Go runtime/metrics 怎样读取运行时指标样本
来源:17golang原创
时间:2026-10-21 20:18:46 169浏览 收藏
读取 Go 运行时指标样本,要先用 metrics.All()取得描述符,再把需要的名称填入 []metrics.Sample,调用 metrics.Read,最后根据 Value.Kind()选择读取方法。不要把所有指标都当成 uint64,也不要把不支持的指标当成数值零。
设想一个报表服务准备升级工具链:原来只记录内存数字,现在还要观察调度等待分布。问题不只是加一个名称,而是采集器能否识别不同类型,以及下一次读取是否会覆盖前一次保留的直方图。下面只解决描述符、样本类型与读取,不扩展成监控平台部署教程。
官方文档:https://pkg.go.dev/runtime/metrics
升级工具链前先发现支持的指标
API 接口稳定不等于每个 Go 实现、每个版本都有完全相同的指标集合。迁移时先依据实际进程支持的描述符选择名称,而不是复制另一台机器的整份清单。
| 旧采集假设 | 迁移后的处理 |
|---|---|
| 名字存在就有值 | 先在 All 返回集合中匹配完整名称 |
| 数字都用 Uint64 读取 | 按照 Value.Kind 分派 |
| 保存 Value 就保存了快照 | 直方图数据需要独立复制 |
Description 包含 Name、Description、Kind 和 Cumulative。Name 的单位后缀属于标识的一部分,不能删掉后再匹配;Kind 指明表示类型,Cumulative 提醒你是否应把它视为累计量。图中的“指标目录”是 All 与 Description,“字段定义”是 Name、Kind、Cumulative;它们是目录和字段的组成关系。

把描述符转换为可读取的样本
以 goroutine 数量和调度延迟分布为例,下面程序先求所需名称与支持集合的交集,未找到的名称明确记录为不支持。保存为 main.go,使用包含 runtime/metrics 的 Go 工具链;程序不依赖第三方包。
package main
import (
"fmt"
"runtime"
"runtime/metrics"
)
func main() {
wanted := []string{
"/sched/goroutines:goroutines",
"/sched/latencies:seconds",
}
supported := make(map[string]metrics.Description)
for _, d := range metrics.All() {
supported[d.Name] = d // 完整名称包含单位后缀
}
samples := make([]metrics.Sample, 0, len(wanted))
for _, name := range wanted {
d, ok := supported[name]
if !ok {
fmt.Printf("不支持: %s\n", name)
continue
}
fmt.Printf("指标=%s 类型=%v 累计=%t\n", name, d.Kind, d.Cumulative)
samples = append(samples, metrics.Sample{Name: name})
}
fmt.Println("工具链:", runtime.Version())
metrics.Read(samples) // 原地填充各样本的 Value
for _, s := range samples {
switch s.Value.Kind() {
case metrics.KindUint64:
fmt.Printf("%s 数值=%d\n", s.Name, s.Value.Uint64())
case metrics.KindFloat64:
fmt.Printf("%s 数值=%g\n", s.Name, s.Value.Float64())
case metrics.KindFloat64Histogram:
h := s.Value.Float64Histogram()
fmt.Printf("%s 桶数=%d 边界数=%d\n", s.Name, len(h.Counts), len(h.Buckets))
case metrics.KindBad:
fmt.Printf("%s 不可用\n", s.Name) // 不能记作零值
default:
fmt.Printf("%s 未处理类型=%v\n", s.Name, s.Value.Kind())
}
}
}
代码展示的是读取方式,不给出伪造的运行数字。读者执行后应看到受支持名称的类型说明和各自对应的数据形式;实际数量、桶数和样本值取决于运行环境。采样一次并不能推出服务是否异常。
读取结果必须按 Kind 分派
Uint64、Float64 和 Float64Histogram 不是任意可互换的转换函数,调用与类型不符的访问器会 panic。即使当前选择的两个指标类型已知,保留 switch 仍能把采集器对类型的假设写清楚。
直方图包含 Counts 与 Buckets:第 i 个计数对应区间 [Buckets[i], Buckets[i+1]),边界数比桶数多一。它不是“边界值对应的精确样本列表”,不能直接把某个边界打印成真实 P99。本文仅读取结构,不实现分位数估算。
Sample 包含 Name 与 Value,Value 的 Kind 决定读取类型;对于直方图,Value 关联 Float64Histogram,后者包含 Counts、Buckets。图中的“样本容器”“类型数据”按这六个实体分组说明。

复用样本时怎样保留独立快照
重复使用同一 samples 切片可减少重复准备,但 Read 正在写入时,其他 goroutine 不应读取或修改关联值。直方图底层存储还可能被后续 Read 复用;只复制 Sample 或保存直方图指针并不能保证独立。
下面是独立复制函数,放在导入 runtime/metrics 的文件中。应在没有并发 Read 的受控位置调用,再把副本交给消费者;复制过程本身不是同步手段。
// 在采样器独占样本时调用,复制两组底层数据
func copyHistogram(h *metrics.Float64Histogram) *metrics.Float64Histogram {
if h == nil {
return nil
}
return &metrics.Float64Histogram{
Counts: append([]uint64(nil), h.Counts...),
Buckets: append([]float64(nil), h.Buckets...),
}
}
一种简单所有权安排是由单个采样 goroutine 管理 samples,在两次 Read 之间提取标量、深拷贝直方图,再通过通道传递自有记录。不要把原始 Value 指针交出去后立刻复用切片,也不要为了避开复制而让消费者无锁读取正在更新的桶计数。
升级前后需要对比什么
迁移清单包括:实际工具链版本、完整指标名称、描述文字、Kind、Cumulative,以及你自己的缺失指标策略。可选指标缺失可以跳过并报告;核心指标缺失是否阻止服务启动由业务明确决定,不能静默填零。
比较累计量时还需注意进程是否重启。累计值是总量,不是当前速率;不同进程直接相减没有意义。直方图差分还需确认两份边界一致,不能仅按相同下标把不同版本的桶相减。采集开销应在真实采样频率下测量,不承诺所有读取都零分配或没有影响。
Read 有错误返回值吗?
没有。单个值的不可用状态通过 KindBad 表达,因此不能靠 err 判断这次选择的所有名称都有效。
能用它查看另一进程吗?
它读取当前 Go 进程的运行时指标,不是操作系统的远程进程查询接口。若需外部访问,要另行设计导出机制和访问边界。
一次 Read 能当作整个系统的严格同时刻快照吗?
不要作这种假设。它用于批量采集所选指标,不应据此宣称所有互不相关指标都形成全局事务快照。
最终应交付一份能发现、分派和保留样本的采集器,而不是固定写死数值类型的指标名称列表。参考:runtime/metrics 文档、Go 官方采样接口源码。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
233 收藏
-
173 收藏
-
385 收藏
-
269 收藏
-
135 收藏
-
265 收藏
-
139 收藏
-
Golang · Go教程 | 1小时前 | go · 数据库 · database/sql · Go database/sql Go Rows.NextResultSet Go 多结果集 Go Rows.Err410 收藏
-
Golang · Go教程 | 1天前 | 并发 · Go教程 · trace.NewTask go tool trace Go runtime/trace 执行跟踪 trace.WithRegion391 收藏
-
184 收藏
-
Golang · Go教程 | 1天前 | 错误处理 · 模板 · Go教程 · html/template · 模板渲染 Go html/template missingkey 模板缺失字段 Execute错误371 收藏
-
326 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习