目录
2.2.6 适当使用 Histogram 和 Summary
一、目的
本规范旨在指导研发团队在开发过程中正确定义、使用和注册自定义监控指标,确保在 Prometheus 监控系统中,所有指标具备一致性、规范性和高可观测性,为系统性能优化和问题排查提供可靠的数据支持。
二、自定义监控指标定义规范
2.1 基本命名规范
2.1.1 指标命名规范
-
前缀: 使用清晰的前缀区分不同业务领域或服务。例如:
-
http_
:与 HTTP 请求相关的指标。 -
db_
:与数据库相关的指标。 -
cache_
:与缓存系统相关的指标。
-
-
命名风格: 使用小写字母和下划线
_
分隔词。指标名称应简单易懂,并反映其所代表的内容。-
示例:
http_request_duration_seconds
,db_query_count
-
-
简洁明了: 指标名称应当简洁、描述性强,便于理解和使用。避免使用复杂、难以理解的名称。例如,
http_requests_total
明确表明它代表 HTTP 请求的总数。 -
使用标准前缀和单位: 遵循 Prometheus 社区推荐的命名惯例,以确保一致性和可读性。常见的后缀包括
_total
表示计数器,_duration_seconds
表示时间度量。确保单位明确,避免混淆。-
示例:
-
request_duration_seconds
: 表示请求的持续时间,单位为秒。 -
disk_io_bytes_total
: 表示磁盘 I/O 的总字节数。
-
-
-
避免缩写: 除非是非常常见的缩写,否则应避免使用缩写来命名指标。例如,
cpu_usage_ratio
要优于cpu_use_rt
。
2.1.2 标签名称
-
规范化名称:
-
标签应使用小写字母,并使用下划线
_
分隔单词。 -
避免大小写混用或混淆的标签命名。
-
示例:
service
,region
,method
,status_code
-
-
标签值的规范性:
-
标签值应具有实际业务意义,能够帮助区分和筛选数据。
-
标签值尽量少变动,避免使用高基数的值如用户ID、请求ID等。
-
2.2 控制基数
控制基数是自定义监控指标设计中的一个关键因素,直接影响 Prometheus 的性能和存储需求。基数(cardinality)指的是某个指标的不同标签组合生成的唯一时间序列数量。过高的基数会导致 Prometheus 需要存储和处理大量的时间序列数据,从而增加存储开销、查询延迟,甚至可能导致系统性能下降或崩溃。因此,在设计自定义监控指标时,务必注意控制基数。
2.2.1 避免高基数标签
-
标签值尽量少变动:
-
使用固定范围的标签值,比如状态码、请求方法、主机名等。
-
避免使用如用户 ID、请求 ID、设备 ID 等高基数标签值,因为这些标签可能会为每个独立请求创建一个新的时间序列。
-
-
限制动态标签:
-
尽量避免使用可能随每次请求而变化的标签,如 URL、查询参数或时间戳等。
-
如果必须使用,可以考虑通过哈希或分桶的方式来降低基数。
-
-
汇总与聚合:
-
考虑将高度动态的标签进行汇总或聚合处理,例如通过
service
、region
、instance
等更高层次的标签来替代过于细粒度的标签。
-
2.2.2 预定义标签集
-
限制标签组合:
-
预先定义好合理的标签集,避免随意添加新的标签组合。这样可以有效控制时间序列的数量。
-
示例:
-
定义好固定的请求方法和状态码标签组合,如
method="GET"
和status_code="200"
,而不是对每个不同的 URL 都创建新的标签组合。
-
-
-
固定标签值范围:
-
对于某些业务场景,可以设定固定的标签值范围。例如,对于请求的响应时间可以分为
fast
、normal
、slow
三类,而不是直接记录具体的时间值。
-
2.2.3 动态数据的处理
-
减少动态数据打点:
-
对于一些动态变化的数据,可以考虑减少其打点的频率,或者在后台通过汇总任务来定期更新指标,而不是实时更新。
-
-
批量处理:
-
对高频率产生的动态数据,可以通过批量处理的方式减少标签的变动。例如,将一分钟内的所有请求统计成一个总量,而不是单独记录每一次请求的详细信息。
-
2.2.4 评估与监控基数
-
定期评估基数:
-
定期检查 Prometheus 中指标的基数情况,识别出可能存在高基数的问题指标,并进行优化调整。
-
-
监控基数变化:
-
实时监控自定义指标的基数,及时发现和处理基数异常增长的情况,避免系统压力过大。
-
2.2.5 降低历史数据的保留
-
合理设置数据保留周期:
-
对于高基数的指标,可以考虑缩短其数据保留周期,减轻存储压力。通过配置
retention
策略,合理设置数据的保留时间。
-
-
历史数据聚合:
-
对历史数据进行聚合存储,将精细的时间序列数据汇总为更为粗粒度的时间段数据,降低历史数据的存储需求。
-
2.2.6 适当使用 Histogram 和 Summary
-
选择适合的指标类型:
-
对于需要监控的数据分布情况,优先使用
Histogram
而非Summary
,因为Histogram
提供的桶可以有效减少标签值的动态变化。
-
-
自定义桶的大小和范围:
-
在使用
Histogram
时,自定义桶的数量和范围,以避免生成过多的时间序列。确保桶的数量和范围合理,既能满足监控需求,又不会导致基数过高。
-
2.2.7 示例
-
避免高基数标签: 高基数标签会生成大量的时间序列,增加存储和检索的负担。避免使用可能产生大量唯一值的标签,例如用户ID、IP地址、请求ID等。
-
Bad: 标签
method
使用过于细化的路径,导致基数过高。status_code{method="/users/123/profile", service="user-service"}
- Good: 使用通用化的
method
标签,减少基数。status_code{method="GET", service="user-service"}
-
2.3 指标类型
Prometheus 支持以下几种主要的指标类型,每种类型有其特定的用途:
-
Counter (计数器): 用于表示某一事件发生的次数。计数器只能递增,或在重启时重置为零。
-
示例:
http_requests_total
-
httpRequestsTotal := prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
})
-
Gauge (仪表盘): 仪表用于记录可以增加或减少的数值,例如当前内存使用量、CPU 使用率等。适合用于表示系统的当前状态。
-
示例:
memory_usage_bytes
-
memoryUsage := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "Current memory usage in bytes.",
})
-
Histogram (直方图): 用于对事件发生的次数或数值进行分布统计,特别适用于测量延迟或请求大小。
-
示例:
http_request_duration_seconds_bucket
-
requestDurationHist := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests in seconds.",
Buckets: prometheus.LinearBuckets(0.01, 0.05, 10), // 0.01s to 0.51s
})
-
Summary (摘要): 类似于 Histogram,但提供百分位数统计,适用于低采样率下的数据。
-
示例:
http_request_duration_seconds
-
2.4 标签 (Labels)
-
标签命名: 标签的命名应遵循与指标类似的命名规范,使用小写字母和下划线
_
。避免使用过多的标签,以减少指标的基数。-
示例:
service
,method
,status
-
-
标签值: 标签值应尽量少变化,并避免使用高基数的值(如用户 ID、请求 ID 等)。标签值应具备实际业务意义,并能够帮助区分不同维度的数据。
2.5 单位 (Units)
-
单位标识: 在指标名称中显式包含单位,使用国际单位制 (SI) 的后缀。典型单位后缀包括:
-
时间:
_seconds
-
大小:
_bytes
-
数量:
_total
-
百分比:
_ratio
或_percent
-
-
示例:
-
http_request_duration_seconds
表示 HTTP 请求的响应时间,单位为秒。 -
memory_usage_bytes
表示内存使用量,单位为字节。
-
三、自定义监控指标的实现
3.1 指标注册
在代码中自定义并注册指标时,应遵循以下步骤:
-
定义指标: 在服务启动时初始化和定义指标对象。选择适当的指标类型,并为其分配标签。
示例 (Go 语言):
var httpRequestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "HTTP request latency distributions.", Buckets: prometheus.DefBuckets, }, []string{"method", "route"}, )
-
注册指标: 将定义的指标注册到 Prometheus 默认的注册表中。
prometheus.MustRegister(httpRequestDuration)
-
记录指标: 在代码逻辑中根据需要更新指标的值。
timer := prometheus.NewTimer(httpRequestDuration.WithLabelValues("GET", "/api/v1/resource")) defer timer.ObserveDuration()
3.2 指标暴露
-
/metrics 端点:
-
确保服务中的指标通过
/metrics
端点暴露,以供 Prometheus 抓取。 -
Prometheus 客户端库通常会自动处理这一部分,只需确保服务端点正确配置。
-
3.3 合理模块化与封装
封装指标创建
将指标的创建与初始化封装在一个独立的模块中,使代码更具可维护性和重用性
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
)
var (
HttpRequestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
})
MemoryUsage = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "memory_usage_bytes",
Help: "Current memory usage in bytes.",
})
...
)
func Init() {
prometheus.MustRegister(HttpRequestsTotal, MemoryUsage)
3.4 优化打点性能
缓存和批处理
根据实际情况,适当缓存数据并进行批处理,减少对指标的频繁操作。
// Example of batch processing
func ProcessDataBatch(batch []Data) {
start := time.Now()
for _, data := range batch {
process(data)
metrics.DataProcessed.Inc()
}
duration := time.Since(start).Seconds()
metrics.BatchProcessDuration.Observe(duration)
}
延迟操作
仅在必要时更新指标,尽量减少对性能的影响。
if needToUpdateMetrics {
metrics.HttpRequestsTotal.Inc()
3.5 文档与注释
注释与文档
为每个指标添加适当的注释和文档,帮助其他开发者理解指标的用途和含义
// HttpRequestsTotal counts the total number of HTTP requests received.
var HttpRequestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests received.",
})