暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【原创|译】Prometheus的Counter、Gauge、Summary、Histogram是如何工作的?(完整版)

工匠人生 2019-11-07
4882

深耕HikariCP、数据库领域的公众号


历史文章推荐:

Grafana JVM Micrometer(4701)指标图解

匠心打磨,《HikariCP数据库连接池实战》书背后的故事(上):创作团队和封面故事

HikariCP常用监控指标与故障排查实战

《【追光者系列】HikariCP连接池监控指标实战》

《【久违了】猪猪带着自己的书《HikariCP In Action》回来了+自序+爆照》

《【文末双十一购书大促】HikariCP可视化监控已支持Prometheus接入


Prometheus仪表中有四种标准度量标准:Gauge, Counter, Summary and Histogram。

Prometheus 四种数据类型

Counter

  • Counter 用于累计值,例如 记录 请求次数、任务完成数、错误发生次数。

  • 一直增加,不会减少。

  • 重启进程后,会被重置。

  例如:http_response_total{method="GET",endpoint="/api/tracks"} 10
  10秒后抓取 http_response_total{method="GET",endpoint="/api/tracks"} 100

复制

Gauge

  • Gauge 常规数值,例如 温度变化、CPU,内存,网络使用变化。

  • 可变大,可变小。

  • 重启进程后,会被重置

  例如:memory_usage_bytes{host="master-01"} 100 < 抓取值
  memory_usage_bytes{host="master-01"} 30
  memory_usage_bytes{host="master-01"} 50
  memory_usage_bytes{host="master-01"} 80 < 抓取值

复制

Histogram

Histogram 可以理解为柱状图的意思,常用于跟踪事件发生(通常是请求持续时间或响应大小)的规模,例如:请求耗时、响应大小。它特别之处是可以对记录的内容进行分组,提供 count 和 sum 全部值的功能。

例如:{小于10=5次,小于20=1次,小于30=2次},count=7次,sum=7次的求和值

复制

Summary

Summary和Histogram十分相似,常用于跟踪事件(通常是要求持续时间和响应大小)发生的规模,例如:请求耗时、响应大小。同样提供 count 和 sum 全部值的功能。
例如:count=7次,sum=7次求值
它提供一个quantiles的功能,可以按%比划分跟踪的结果。例如:quantile取值0.95,表示取采样值里面的95%数据。

一、Counter

Counter用来计数。

其实计数本身并没有太多用处,用户真正的诉求是计数能有多快,比如你当前正在每秒收到12个请求。如果在每次请求时都将Counter增加,那么如何在监视系统中找出它,以便对其进行图形显示并发出警报?

有三种常见的做法。

第一种方法是定期提取流向监视系统的当前值,比如每分钟提取一次,然后将计数器重置为0。这有一个问题,如果推送失败,那么您将丢失关于该时间段的所有信息。这可能让你看不到流量的微小爆炸。另外,如果您有两个系统从计数器中提取数据以实现冗余,那么每个系统只能看到大约一半的增量,那不是很好的方案。

第二种方法是使用某种形式的移动平均值,通常是指数型的。这意味着最近的数据点比旧的数据点更重要。根据增量模式的相位和频率,相对于监控系统采样信息的时间,因为并非所有的数据点都是相同的,所以将会得到不同的结果。这种方法可以处理多个采集样本的系统,但是如果一个样本没有被采集,就会丢失信息。和第一种方法相比,这种方法更好一些,但还远远不够完美。

Prometheus采用的是第三种方法。计数器从0开始递增。客户端client不做其他计算。每一次scrape(Prometheus间隔,类似于雨刮器的刮擦),Prometheus就取一次这种状态的样本。在Prometheus中的 rate()
 方法查看一段时间内的时间序列历史,并计算它每秒增长的有多快这种方法可以处理多个采集样本的Prometheus Server,如果scrape失败会丢失分辨率,但是并不是数据(因为下一次成功的scrape中,increments增量并没有丢失或者平均掉)

一个常见的问题是,当进程重新启动并将计数器重置为0时会发生什么? rate()
 将自动处理这个问题。每当计数器Counter出现减少的情况时,它将被视为在第一个数据点之后立即重置为0。这使得计数器不可能递减变得很重要,一个有可能递减的计数器 Counter
 实际上是一个Gauge
。其他的监测系统经常在计数器 Counter
 复位时丢弃信息,比如Graphite的nonNegativeDerivative功能。rate()
 还具有处理传递给 rate()
的不完全覆盖整个时间范围的样本的逻辑。

除了 rate()
 之外,还有 increase()
 和irate()
 这两个对计数器 Counter
 进行操作的函数。rate()
 返回每秒的值,increase()
 是一个方便的函数,它返回时间段内的总数。例如,increase(my_counter_total[1h])
 将返回计数器Counter每小时的增量。以前讨论过irate()
,它只查看最后两个样本,对于短时间内的高精度图非常有用。resets()
 告诉您计数器Counter重置的频率,这对于调试非常有用。

最后,虽然我们在这里只讨论了计数请求,计数器Counter可以用任何非负的浮点值递增。您可以使用它们来跟踪时间花费,字节处理,异常跑出或记录遍历。

更多资料可以查看 柏林的CloudNativeCon 2017 ,对此主题进行了更深入的演讲
https://www.youtube.com/watch?v=67Ulrq6DxwA


二、Gauge

Prometheus Gauge
 原理和其他监测系统中的 Gauge
 一样简单。随着时间的推移, Gauge
可以上下浮动,而 scrape
值 则可以对当前值进行快照。像计数器Counter
 一样,移动平均值或重置是不可能的。

Prometheus不同于其他监控系统的是 Gauge
的API。Prometheus的Gauge
API涵盖了所有Gauge
的使用场景在一个地方使用的情况。它有 inc
dec
set
方法,以及各种常用的实用功能(如设置当前时间)。客户端client库library负责所有的bookkeeping和线程安全。通常也有一些方法来做回调,比如Python中的 set_function
,Java中的 setChild
,Go中的 GaugeFunc

相比之下,流行的Dropwizard metric library提供了许多不同的 gauge
 类,主要基于您希望它在暴露gauge
 之前执行什么处理。我认为有两个类是core核心的,因为它们不做任何额外的处理。Dropwizard Gauge
 有一个基于回调的API。Dropwizard计数器 Counter
 有 inc
 和 dec
方法,这实际上使它成为普罗米修斯的一个gauge
。其他Dropwizard gauge
 类型的用例在Prometheus有不同的处理方式,例如Dropwizard Meter是一个结合PromQL rate
功能为例的Prometheus Counter。

与许多其他监视和仪表系统相比,还有一个不同之处。与Prometheus一样,完全支持64位浮点值。

PromQL提供了许多处理gauge
的函数和操作符。事实上,除了少数只与计数器Counter
一起使用的函数(如rate
)之外,所有函数都可以与gauge
一起合理使用。值得注意的函数包括使用简单线性回归来计算值的变化率的deriv
,以及更进一步并预测未来的predict_linear
。使用max
min
avg
等操作符可以跨系列聚合,使用max_over_time
min_over_time
avg_over_time
等函数可以跨时间聚合。


三、 Summary

Summary
是其他类型的组合,以简化通用模式的使用。一个Summary
由两个 Counter
和一些可选的 Gauge
 组成。Summary
的 Metrics
 用于通过它们的观察observe方法跟踪事件的大小,通常是它们花费的时间。通常还会有一些实用工具来简化记录时间。

让我们来看看exposition格式的例子:

# HELP prometheus_rule_evaluation_duration_seconds The duration for a rule to execute.
# TYPE prometheus_rule_evaluation_duration_seconds summary
prometheus_rule_evaluation_duration_seconds{quantile="0.5"} 6.4853e-05
prometheus_rule_evaluation_duration_seconds{quantile="0.9"} 0.00010102
prometheus_rule_evaluation_duration_seconds{quantile="0.99"} 0.000177367
prometheus_rule_evaluation_duration_seconds_sum 1.623860968846092e+06
prometheus_rule_evaluation_duration_seconds_count 1.112293682e+09

复制

_sum
 和 _count
 几乎总在那里,它们其实都是计数器 Counter
_count
 在每次 observe
 上增加1,并且_sum
 会根据观察值上增加。从这个过程开始到现在,已经有大约10亿次观测了,总共花了160万秒。这本身并没有太大的用处,但我们可以像使用其他计数器Counter
一样使用rate()
rate(prometheus_rule_evaluation_duration_seconds_count[5m])
 是最近5分钟平均的每秒的观察次数,rate(prometheus_rule_evaluation_duration_seconds_sum[5m])
 是平均每秒花费的时间。如果将这些相除,就得到了一次观察的平均持续时间:

rate(prometheus_rule_evaluation_duration_seconds_sum[5m]
/rate(prometheus_rule_evaluation_duration_seconds_count[5m])

复制

有一点要注意的是,如果是在一段时间内没有观测值,那么它将返回NaN的。这个除以0是有意义的。如果您发现这妨碍了其他的数学计算,请记住,您不应该对平均值求平均,相反,你应该做所有你需要的聚合,然后最后分割。

不带分位数的整体 Summary
是一种跟踪延迟、每次请求传输的数据量、记录访问等的好方法,因为每个标签集 labelset
 只使用两个时间序列。

带quantile标签的样本会稍微复杂一些,通常不会出现。这些是观测值的分位数,因此 {quantile="0.9"}
 样本表明第90个百分位数在100us左右。这是什么时间段?

这是在客户端计算分位数的主要问题之一。如果取用进程开始时的分位数,那么随着时间的推移,样本与当前条件的相关性就会越来越小。一旦你拥有有了一个分位数,也就不可能进行任何数学运算或聚合,因此,鉴于时间序列的历史,无法将所有内容拼接在一起。Prometheus客户端client库library通常的做法是在内存中保留10个分位数对象。所有观察值发送到全部10个对象,每个跟踪对象从下一个的1分钟开始,最早的一个将包含多达10分钟的样本。一旦最老的对象太旧了,它就会被删除,并启动一个空的分位数对象。这样做的最终结果是,Prometheus客户端库Summary
返回的分位数以1分钟的粒度,超过了过去10分钟左右的观测值。如果在一段时间内没有样本,那么将为分位数返回NaN,就像用_sum
除以上面的_count
一样。

这的确确保了最近的观察结果不会被较早的信息淹没,然而,像所有客户端client-side的分位数系统一样,它在Go中的相对昂贵,比两个计数器Counter
慢约45倍(21ns VS 930ns)。它的优点是您不必事先知道观测值的分布。总的来说,鉴于成本,并且您无法进一步汇总此类指标,因此建议您节省使用Summary
与分位数一起使用。这个特性的存在比任何事情都更具有历史意义。

四、Histogram

histogram
 和 summary
 有一些相似之处。histogram
 是各种 counter
的结合。和 summary
 metrics相似,histogram
 metrics用于跟踪事件的大小,通常是它们花费的时间,根据 observe
方法判断。就像对summary
一样,通常也有精确的实用程序使它很容易记时,它们的不同之处在于对分位数的处理。

这是一个来自Prometheus本身的展示格式 exposition format
 的例子,它碰巧也有一个handler
标签:

# HELP prometheus_http_request_duration_seconds Histogram of latencies for HTTP requests.
# TYPE prometheus_http_request_duration_seconds histogram
prometheus_http_request_duration_seconds_bucket{handler="/",le="0.1"} 25547
prometheus_http_request_duration_seconds_bucket{handler="/",le="0.2"} 26688
prometheus_http_request_duration_seconds_bucket{handler="/",le="0.4"} 27760
prometheus_http_request_duration_seconds_bucket{handler="/",le="1"} 28641
prometheus_http_request_duration_seconds_bucket{handler="/",le="3"} 28782
prometheus_http_request_duration_seconds_bucket{handler="/",le="8"} 28844
prometheus_http_request_duration_seconds_bucket{handler="/",le="20"} 28855
prometheus_http_request_duration_seconds_bucket{handler="/",le="60"} 28860
prometheus_http_request_duration_seconds_bucket{handler="/",le="120"} 28860
prometheus_http_request_duration_seconds_bucket{handler="/",le="+Inf"} 28860
prometheus_http_request_duration_seconds_sum{handler="/"} 1863.80491025699
prometheus_http_request_duration_seconds_count{handler="/"} 28860

复制

_sum
_count
的工作方式与summary
完全相同,它们可以用于生成过去五分钟内的平均持续时间:

  rate(prometheus_http_request_duration_seconds_sum[5m]
/ rate(prometheus_http_request_duration_seconds_count[5m])

复制

很少出现_sum
不存在的情况,例如来自MySQLd exporter的某些Metrics。

histogram
中最有趣的部分是 _bucket
时间序列,它是histogram
的实际 histogram
 部分。更特别的是,它们是组成累积 histogram
 的计数器 counters
 ,le
 表示小于或等于。所以26688个请求小于等于200ms, 27760个请求小于等于400ms,总共有28860个请求。buckets
 中的值将是单调非递减的,而+Inf buckets
 的值最大。+Inf buckets
必须始终存在,并且将匹配_count
的值。

要计算0.9分位数(第90个百分位数),可以使用:

histogram_quantile(0.9
  rate(prometheus_http_request_duration_seconds_bucket[5m])
)

复制

histograms
相对于 summarys
的一大优势是,可以在计算分位数前把 buckets
 数加起来——注意不要丢失 le
 标签。

histogram_quantile(0.9
  sum without (handler)(
    rate(prometheus_http_request_duration_seconds_bucket[5m])
  )
)

复制

除了可聚合外,histograms
 在客户机client上也更便宜,因为Counter
增加得更快。为什么不用histograms
呢?有一个很长的答案,但简短的版本是,使用histograms
,您必须预先选择buckets
,并且由于桶基数,成本从客户端转移到Prometheus本身。默认的10个buckets
覆盖了一个典型的web服务,其延迟为毫秒到第二个范围,有时您需要调整它们。例如,为了更好地跟踪PromQL的请求它们被覆盖了,而PromQL有两分钟的默认超时。拥有10个以上的buckets
将会给出更精确的结果,但是它也会增加很多时间序列。特别是与其他标签Label
组合在一起的时候。

像Prometheus这样的实时监视系统,目标应该是提供足够好的值以作出工程决策。例如,知道第90个百分位延迟增加了50ms,比知道在调用时该值现在是562ms还是563ms更为重要,十个buckets
就足够了。如果您需要一个完美的答案,你以后可以从你的日志系统中计算出来。如前所述,如果有过多的buckets
,则可以在摄取时将其丢弃。在更极端的情况下,可以完全忽略 _bucket
 系列,而是依赖于_sum
_count
的平均值。

总之,histograms
 允许对分位数进行汇总计算,不过您需要对基数保持一定的警惕。请记住,如果您真的不需要histograms
 ,那么不包含分位数的Summary
是一种便宜的选择。


欢迎大家购买我的新书《HikariCP数据库连接池实战》



《HikariCP数据库连接池实战》京东的数据库专题上线,轮转推荐


https://pro.m.jd.com/mall/active/2sfniwqTJWrqUQbWozxWvWrAW7tk/index.html


豆瓣书评:

https://book.douban.com/subject/34659722/


中关村软件园1024无bug市集现场书展


双十一大促,促销费用都是出版社来承担,京东双十一期间100-50元,预购从速


京东购书二维码:

欢迎转发海报:





你点的每个“在看”,我都认真当成了喜欢
复制
文章转载自工匠人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论