13

prometheus的rate与irate内部是如何计算的

 3 years ago
source link: https://zhangguanzhang.github.io/2020/07/30/prometheus-rate-and-irate/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

prometheus的rate与irate内部是如何计算的



字数统计: 905阅读时长: 4 min
 2020/07/30  596  Share

市面上的翻译误导人,压根不是啥平均增长率,看了下源码和实际算下来让大家好理解

主要代码是在 https://github.com/prometheus/prometheus/blob/master/promql/functions.goextrapolatedRatefuncRate,funcRate为

1
2
3
func funcRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
return extrapolatedRate(vals, args, enh, true, true)
}

它的前后还有funcDeltafuncIncrease对应promql的deltaincrease,这俩函数内部都是调用的extrapolatedRate,主要区别是通过向extrapolatedRate函数传递最后的两个布尔标志位的差异,来在extrapolatedRate内部进行差异化计算,也就是说ratedeltaincrease的部分数学计算逻辑是一样的。

funcRateextrapolatedRate最后俩实参格式为isCounter bool, isRate bool,所以rate只能用在counter的 metrics 类型上进行计算。

数据点的选取

先看这段代码

1
2
3
4
5
6
7
8
9
10
11
var (
counterCorrection float64
lastValue float64
)
for _, sample := range samples.Points {
if isCounter && sample.V < lastValue {
counterCorrection += lastValue
}
lastValue = sample.V
}
resultValue := lastValue - samples.Points[0].V + counterCorrection

counterCorrection是字面意思修正数值,counter会reset,例如exporter重启了。例如60秒内有下面6数值,在第四个数字后面发生了重置

1
2 4 6 8 2 4

2小于lastValue 8,所以counterCorrection = 8

最后的 resultValue = 4 + 8 - 2,当然,重置的情况很少,这里如果不重置用数据2 4 6 8 10 12算就是最后一个值减去第一个值resultValue = 12 - 2 + 0和重置算得一样

计算的算式

是结果除以时间的秒数

1
2
3
if isRate {
resultValue = resultValue / ms.Range.Seconds()
}

对比下irate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 取最后一个数据点
lastSample := samples.Points[len(samples.Points)-1]
// 取倒数第二个数据点
previousSample := samples.Points[len(samples.Points)-2]

var resultValue float64
if isRate && lastSample.V < previousSample.V {
// counter重置则取最后一个值.
resultValue = lastSample.V
} else {
// 最后一个点数值 - 倒数第二个数值
resultValue = lastSample.V - previousSample.V
}
// 最后两个点的时间间隔
sampledInterval := lastSample.T - previousSample.T
if sampledInterval == 0 {
// Avoid dividing by 0.
return out
}

if isRate {
// 转换成秒,然后结果除以秒数
resultValue /= float64(sampledInterval) / 1000
}

官方文档和市面上的 gitbook 都是把rate翻译成增长率是错误的,应该是平均每秒增长了多少数值。按照实践来算下,同时查询node_time_seconds[1m]rate(node_time_seconds[1m])。我们手动计算下看看是否和rate的结果一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ node_time_seconds[1m]
node_time_seconds{instance="exporter:9100",job="node-resources"}
1596077182.3093214 @1596077182.307 // 第一个点
1596077192.3132203 @1596077192.307
1596077202.311446 @1596077202.307
1596077212.309673 @1596077212.307
1596077222.316771 @1596077222.307
1596077232.3151288 @1596077232.307 // 最后一个点
node_time_seconds{instance="10.0.23.29:9100",job="node-resources"}
1596077178.6314309 @1596077178.633 // 第一个点
1596077188.6312084 @1596077188.633
1596077198.633293 @1596077198.634
1596077208.6332283 @1596077208.634
1596077218.6320524 @1596077218.633
1596077228.635078 @1596077228.633 // 最后一个点

$ rate(node_time_seconds[1m])
{instance="exporter:9100",job="node-resources"} 1.0001161479949952
{instance="10.0.23.29:9100",job="node-resources"} 1.0000729417800902

先用10.0.23.29这个 instance 算,

1
2
3
4
(1596077228.635078 - 1596077178.6314309) / (1596077228.633 - 1596077178.633)
// web上的时间是秒数的,go的time是多了三个单位,所以代码里/1000转换成秒这里不需要除以1000
上面式子左边和右边算是下面结果:
50.003647089 / 50 = 1.00007294178

谷歌搜的在线计算器算的(比windows的calc精度高一些),由于是float64,所以精度丢失了一些。结果一样。再算下另一个 instance

1
2
3
(1596077232.3151288 - 1596077182.3093214) /
(1596077232.307 - 1596077182.307)
50.0058073997 / 50 = 1.00011614799

increase是最后一个点减去第一个点,不除以秒数。所以在 counter 没发生重置情况下,下面两个是相等的

1
increase(node_time_seconds[1m]) / 60 == rate(node_time_seconds[1m])

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK