2. Metric 类型与采集:Counter、Gauge、Histogram、Summary 的区别与适用场景,Prometheus Exporter 采集原理
2.1 四大核心 Metric 类型
Prometheus 定义了四种核心 Metric 类型,每种类型对应不同的数据特征与查询模式。理解它们的差异是设计可靠告警与容量水位阈值的前提。
| 类型 | 语义 | 值特征 | 典型场景 | 查询函数 |
|---|---|---|---|---|
| Counter | 只增不减的累计计数器 | 单调递增,重启归零 | 请求总数、错误总数、CPU 时间 | rate()、increase() |
| Gauge | 可增可减的瞬时值 | 上下波动,反映当前状态 | 内存使用量、连接数、温度 | avg()、max()、min() |
| Histogram | 分桶统计的分布直方图 | 多个 bucket 计数器 + 总和 + 计数 | 请求延迟分布、响应大小分布 | histogram_quantile() |
| Summary | 预计算的分位数摘要 | 客户端侧计算 φ 分位数 + 总和 + 计数 | SLI 延迟分位数(0.5/0.9/0.99) | 直接读取 _quantile 标签 |
关键原则:Counter 必须配合
rate() 或 increase() 使用,直接对 Counter 做值告警几乎总是错误的;Gauge 适合设置静态阈值;Histogram 与 Summary 都用于延迟分析,但 Histogram 支持服务端动态聚合,Summary 则无法跨实例聚合。
2.2 类型选择决策树
在实际 Metric 设计时,按以下顺序判断应使用哪种类型:
- 数据是否只增不减? → 是 → Counter(请求量、错误量、写入字节数)
- 数据是否可增可减,反映当前状态? → 是 → Gauge(内存、连接数、队列深度)
- 是否需要分析分布或计算分位数? → 是
- 是否需要跨实例聚合分位数? → 是 → Histogram(服务端聚合)
- 是否已知固定分位数且无需跨实例聚合? → 是 → Summary(客户端预计算)
2.3 Histogram 与 Summary 的深层对比
Histogram 和 Summary 都用于度量延迟分布,但架构差异显著,直接影响容量压测时的水位口径选择。
| 维度 | Histogram | Summary |
|---|---|---|
| 分位数计算位置 | 服务端(Prometheus Server) | 客户端(Exporter 内) |
| 聚合能力 | 支持跨实例 histogram_quantile() 聚合 | 不支持聚合,只能查看单实例 |
| 存储开销 | 由 bucket 数量决定(通常 10~15 个) | 由分位数数量决定(通常 3~5 个) |
| 精度控制 | 受 bucket 边界影响,边界内线性插值 | 客户端精确计算,无插值误差 |
| 适用场景 | 全局延迟 SLO、多实例聚合告警 | 单实例精确分位数、固定分位数需求 |
容量压测建议:在压测水位评估中优先使用 Histogram,因为它允许在压测结束后通过调整 bucket 边界重新计算分位数,无需重新采集数据。Summary 一旦采集,分位数即固定,无法回溯调整。
2.4 Prometheus Exporter 采集原理
Exporter 是 Prometheus 生态中负责将第三方系统指标转换为 Prometheus 可抓取格式的代理组件。其核心采集流程如下:
- 注册 Metric 描述符:在 Exporter 初始化时,通过
prometheus.NewCounterVec()、prometheus.NewGaugeVec()等函数定义 Metric 的名称、标签、帮助信息。 - 采集器(Collector)实现:实现
prometheus.Collector接口的Describe()和Collect()方法。Describe()发送 Metric 描述,Collect()发送当前样本值。 - HTTP Handler 暴露:通过
promhttp.Handler()将采集到的 Metric 序列化为 Prometheus 文本格式,在/metrics端点暴露。 - 抓取与拉取模型:Prometheus Server 定期向 Exporter 的
/metrics发起 HTTP GET 请求,拉取文本数据并解析入库。
2.5 自定义 Exporter 代码示例(Go)
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
// Counter:累计请求数
requestCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "status"},
)
// Gauge:当前连接数
activeConnGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "active_connections",
Help: "Current number of active connections",
},
)
// Histogram:请求延迟分布
requestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request latency distributions",
Buckets: prometheus.DefBuckets, // 默认桶:[0.005, 0.01, 0.025, 0.05, ..., 10]
},
[]string{"method"},
)
)
func init() {
prometheus.MustRegister(requestCounter)
prometheus.MustRegister(activeConnGauge)
prometheus.MustRegister(requestDuration)
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
采集避坑:Exporter 的
Collect() 方法必须快速返回,避免阻塞 Prometheus 抓取。如果采集逻辑涉及远程调用或复杂计算,建议使用 prometheus.NewDesc() 配合异步更新模式,将采集与暴露解耦。
2.6 采集性能与水位口径的关系
在容量压测中,Exporter 的采集性能直接影响水位数据的准确性:
- 采集延迟:如果 Exporter 采集耗时超过
scrape_timeout(默认 10s),Prometheus 会丢弃该次样本,导致水位数据出现空洞。 - 样本量控制:每个 Exporter 暴露的 Metric 数量建议控制在 10,000 以内,超过此阈值会显著增加 Prometheus Server 的解析与存储压力。
- 标签基数:避免高基数标签(如 user_id、request_id),否则会导致 Histogram 的 bucket 数量爆炸,压测水位评估失真。
| 采集维度 | 推荐值 | 超限影响 |
|---|---|---|
| 单 Exporter Metric 数量 | < 10,000 | 抓取超时、内存 OOM |
| 单 Metric 标签基数 | < 100 | 存储膨胀、查询缓慢 |
| 采集间隔 | 15s ~ 30s | 过短增加负载,过长丢失细节 |
| Histogram bucket 数 | 10 ~ 15 | 过多增加存储与计算开销 |
水位口径优化建议:在压测前,先通过
promtool tsdb analyze 分析现有 Exporter 的采集性能与标签基数,识别出高基数 Metric 并提前降维或删除,确保压测期间水位数据的完整性与准确性。