Prometheus云原生监控:运维与开发实战
上QQ阅读APP看书,第一时间看更新

4.3.1 计数器

计数器类型代表一种样本数据单调递增的指标,在没有发生重置(如服务器重启、应用重启)的情况下只增不减,其样本值应该是不断增大的。例如,可以使用Counter类型的指标来表示服务的请求数、已完成的任务数、错误发生的次数等。计数器指标主要有两个应用方法。


1)Inc()        // 将Counter值加1
2)Add(float64) // 将指定值加到Counter值上,如果指定值小于0,会产生Go语言的panic异常
 // 进而可能导致崩溃

但是,计数器计算的总数对用户来说大多没有什么用,大家千万不要将计数器类型应用于样本数据非单调递增的指标上,比如当前运行的进程数量、当前登录的用户数量等应该使用仪表盘类型。

为了能够更直观地表示样本数据的变化情况,往往需要计算样本的增长速率,这时候通常使用PromQL的rate、topk、increase和irate等函数,如下所示。


rate(http_requests_total[5m]) // 通过rate()函数获取HTTP请求量的增长速率
topk(10, http_requests_total) // 查询当前系统中访问量排在前10的HTTP地址

如上所示,速率的输出rate(v range-vector)也应该用仪表盘来承接结果。

在上面的案例中,如果有一个标签是Device,那么在统计每台机器每秒接受的HTTP请求数时,可以用如下的例子进行操作。


sum without(device)(rate(http_requests_total[5m]))

当然,在上述命令中你可以根据实际的业务需求聚合更多的标签来应对具体工作场景。

实战陷阱

PromQL要先执行rate()再执行sum(),不能执行完sum()再执行rate()。

这背后与rate()的实现方式有关,rate()在设计上假定对应的指标是一个计数器,也就是只有incr(增加)和reset(归零)两种行为。而执行了sum()或其他聚合操作之后,得到的就不再是一个计数器了。举个例子,比如sum()的计算对象中有一个归零了,那整体的和会下降,而不是归零,这会影响rate()中判断reset(归零)的逻辑,从而导致错误的结果。

写PromQL时,这个坑容易避免,但碰到Recording Rule就不那么容易了,因为不去看配置的话很难发现这个问题。因此,Recording Rule规定:一步到位,直接算出需要的值,避免算出一个中间结果再拿去做聚合。

increase(v range-vector)函数传递的参数是一个区间向量,increase函数获取区间向量中的第一个和最后一个样本并返回其增长量。下面的例子可以查询Counter类型指标的增长速率,可以获取http_requests_total在最近5分钟内的平均样本,其中300代表300秒。


increase(http_requests_total[5m]) / 300

实战陷阱

rate和increase函数计算的增长速率容易陷入长尾效应中。比如在某一个由于访问量或者其他问题导致CPU占用100%的情况中,通过计算在时间窗口内的平均增长速率是无法反映出该问题的。

为什么监控和性能测试中,我们更关注p95/p99位?就是因为长尾效应。由于个别请求的响应时间需要1秒或者更久,传统的响应时间的平均值就体现不出响应时间中的尖刺了,去尖刺也是数据采集中一个很重要的工序,这就是所谓的长尾效应。p95/p99就是长尾效应的分割线,如表示99%的请求在XXX范围内,或者是1%的请求在XXX范围之外。99%是一个范围,意思是99%的请求在某一延迟内,剩下的1%就在延迟之外了。只是正推与逆推而已,是一种概念的两种不同描述。

irate(v range-vector)是PromQL针对长尾效应专门提供的灵敏度更高的函数。irate同样用于计算区间向量的增长速率,但是其反映出的是瞬时增长速率。irate函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率的。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度。通过irate函数绘制的图标能够更好地反映样本数据的瞬时变化状态。irate的调用命令如下所示。


irate(http_requests_total[5m])

实战陷阱

irate函数相比于rate函数提供了更高的灵敏度,不过分析长期趋势时或者在告警规则中,irate的这种灵敏度反而容易造成干扰。因此,在长期趋势分析或者告警中更推荐使用rate函数。