全文转载自:深入理解和使用 Prometheus 的 Histogram 指标类型
Prometheus的数据格式和指标类型
Prometheus 会将所有采集到的样本数据以时间序列的方式保存在内存数据库中,并且定时保存到硬盘上。时间序列是按照时间戳和值的序列顺序存放的,我们称之为向量(vector),每条时间序列通过指标名称(metrics name)和一组标签集(labelset)命名。如下所示,可以将时间序列理解为一个以时间为 X 轴的数字矩阵:
^
│ . . . . . . . . . . . . . . . . . . . node_cpu_seconds_total{cpu="cpu0",mode="idle"}
│ . . . . . . . . . . . . . . . . . . . node_cpu_seconds_total{cpu="cpu0",mode="system"}
│ . . . . . . . . . . . . . . . . . . node_load1{}
│ . . . . . . . . . . . . . . . . . .
v
<------------------ 时间 ---------------->
在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:
- 指标(metric):指标名和描述当前样本特征的标签集合
- 时间戳(timestamp):一个精确到毫秒的时间戳
- 样本值(value): 一个 float64 的浮点型数据表示当前样本的值
指标类型一般分为四类。
- Counter
计数器,用于保存计数型数据,如网站访问量等。 - Gauge
仪表盘,用于存储有着起伏特征的指标数据,如空间空闲大小等。 - Summary
摘要,Histogram的扩展类型,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间计算 - Histogram
直方图,在一段时间范围内对数据进行采样,并将其计入可配置的存储中,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。
Counter 和 Gauge比较好理解,而 Histogram 类型则是相对来说复杂一些的。理解 Histogram 类型是能够帮我们更好地使用 Prometheus 监控,尤其是对于调用耗时P99/P95这一类需要设置分位监控的数据。
Histogram类型
Histogram 中文的含义是直方图,我们在学习概率统计的时候都学习过直方图。直方图是对数据如何分布的一种总结方式——有多少值是高的,有多少是低的,有多少介于两者之间。
image.png如图所示,Histogram 显示了数据集的分布。有些值低(<=10),有些值中等(>10 和 <=100),有些值高(>100 和 <=1000)。直方图根据这些范围将数据分组到存储桶bucket中,并计算每个存储桶中有多少值。这使我们对数据如何在其值范围内分布有所了解。在决定如何绘制直方图时,通常会选择对数据敏感且对分析有意义的bucket范围。
现在来让我们看一下 Prometheus Histogram。 Prometheus Histogram在三个方面与上述示例略有不同:
-
桶是累积的——也就是说,每个桶包含的值小于或等于桶的上限阈值
image.png每个桶都比之前的大,并且包含所有值的总和,直到桶的阈值。从左往右来看,一定是非递减的。
-
Prometheus 直方图度量也是一个时间序列
Prometheus 每隔一段时间从进程中抓取指标。每次它抓取一个直方图指标时,它都会收到一个类似于上面的直方图——一个带有“小于或等于”桶的累积直方图。
重要的是当查询直方图时,需要处理直方图的时间序列。直方图指标本身是包含一个值范围的——每个发生数据收集的时间点会有一个值——每个这样的值代表一个类似于上面的直方图。
每个直方图值(从上一次收集到这次收集的时间间隔)总结了自上次收集以来进程记录的值的分布。
- 时间序列本身是累积的
每次 Prometheus 抓取直方图时,这些值都不会重置。这意味着每个桶中的计数在指标的整个生命周期内是累积的(至少在每个进程的内存中),并且实际上每个桶的值的变化告诉我们自上次抓取以来观察值的分布。
下面写个demo代码验证:
package main
import (
"log"
"math/rand"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
// 定义一个Histogram类型的指标
histogram := promauto.NewHistogram(prometheus.HistogramOpts{
Name: "histogram_showcase_metric",
Buckets: []float64{5.0, 10.0, 20.0, 50.0, 100.0}, // 根据场景需求配置bucket的范围
})
go func() {
for {
// 这里搜集一些0-100之间的随机数
// 实际应用中,这里可以搜集系统耗时等指标
histogram.Observe(rand.Float64() * 100.0)
time.Sleep(1 * time.Second)
}
}()
// 指标上报的路径,可以通过该路径获取实时的监控数据
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
}
打开:http://192.168.48.20:8080/metrics
如果本地安装了Prometheus,并且配置了对应的服务采集地址,那么 就可以在通过Prometheus自带的可视化界面,看到如下收集的数据
image.png那么,我们该如何利用该指标进行查询呢?
-
每秒进行请求次数
可以看到 Histogram 类型的数据还会收集了一个 _count 类数据,我们可以用它来了解每秒监控的频率。可用于观测每秒收到多少请求。使用以下 Prometheus 查询可以进行观察:
image.pngrate(histogram_showcase_metric_count[1m])
因为,我们每秒上报1次(time.sleep),所以算出来每秒新增1个左右也是合理的。
-
观测分布
这是实际使用中,我们最需要 Histogram 的地方。我们有一些高频事件(再次以Web请求来举例),会在 Prometheus 的每个抓取间隔之间进行了大量数据采集(例如请求持续时间),然后使用Histogram来记录观察结果,我们想查询该度量,以深入了解值在抓取间隔内和随时间的分布情况。现在来逐步构建查询。首先,使用 histogram_showcase_metric_bucket 查询指标:
image.pnghistogram_showcase_metric_bucket
然后,使用 histogram_showcase_metric_bucket[1m] 从最后一分钟的指标中获取一系列值:
image.pnghistogram_showcase_metric_bucket[1m]
通过使用 rate(histogram_showcase_metric_bucket[1m]) 查看每个 bucket 的每秒变化率来了解这些 bucket 是如何随时间变化的:
image.pngrate(histogram_showcase_metric_bucket[1m])
这里展示了在最后一分钟在每个 bucket 中进行观测的频率。
最后,我们可以使用 Prometheus 的函数 histogram_quantile() 将这些信息转化为更直观有价值的数据。因为我们有每个直方图 bucket 的变化率,Prometheus 现在可以计算出哪个 bucket 标签包含给定的分位数(例如第 95 个百分位数)。这意味着我们现在可以找出数据中表示给定分位数的近似值。例如
image.pnghistogram_quantile(0.95, rate(histogram_showcase_metric_bucket[1m]))
在这里可以看到,在最后一分钟,第 95 个百分位处的观测值的近似值为 95.37。这里因为我们的耗时是0-100模拟随机生成的,所以这个近似值是非常合理的。
使用和建议
-
接口的QPS
接口的QPS = query per second, 每秒接收的请求量,这个是反映业务指标的。
我们一般用counter定义指标收集,但是也可以直接对 Histogram 收集的count进行查询处理。 -
接口的耗时
接口的耗时,是反映接口性能的最关键因素。因为现在后端服务的请求链路都是比较长的,所以做好延时的监控对于问题排查和后续优化有很重要的指导意义。
参考资料
1、https://juejin.cn/post/7152837166190739486
2、 https://cloud.tencent.com/developer/article/1495303
网友评论