Prometheus-rate与irate源码详解

本文详细解析了Prometheus监控系统中的速率计算函数irate与rate的工作原理。介绍了irate如何基于时间序列数据计算瞬时变化率,而rate则适用于计算计数器类型的指标速率,并通过实例说明了不同时间窗口下速率计算的具体过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们先看prometheus的函数格式

一 类型

var Functions = map[string]*Function{
    ...
	"rate": {
		Name:       "rate",
		ArgTypes:   []ValueType{ValueTypeMatrix},
		ReturnType: ValueTypeVector,
		Call:       funcRate,
	},
	...
	"irate": {
		Name:       "irate",
		ArgTypes:   []ValueType{ValueTypeMatrix},
		ReturnType: ValueTypeVector,
		Call:       funcIrate,
	},
	...
}
func getFunction(name string) (*Function, bool) {
	function, ok := Functions[name]
	return function, ok
}
//通过getfunc 传递name 我们拿到func 并且调用

二 irate

那么我们具体看一下rate是如何实现的

func funcIrate(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
	return instantValue(vals, enh.out, true)
}
func instantValue(vals []Value, out Vector, isRate bool) Vector {
	samples := vals[0].(Matrix)[0]
	/*
	type Matrix []Series
	type Series struct {
		Metric labels.Labels `json:"metric"`
		Points []Point       `json:"values"`
	}
	type Point struct {
		T int64
		V float64
	}
	type Vector []Sample
	type Sample struct {
		Point
		Metric labels.Labels
	}
	也就是说samples = Series  type
	 */
	// No sense in trying to compute a rate without at least two points. Drop
	// this Vector element.
	//试图计算一个没有至少两个点的速率是没有意义的。删除这个向量元素
	if len(samples.Points) < 2 {
		return out
	}
	// 2个值以上
	//倒数第一个值
	lastSample := samples.Points[len(samples.Points)-1]
	//倒数第二个值
	previousSample := samples.Points[len(samples.Points)-2]
	//声明一个新的value
	var resultValue float64
	//如果是rate 并且倒数第一个值小于倒数第二个值
	if isRate && lastSample.V < previousSample.V {
		// Counter reset.
		//那么 把倒数第一个值赋予我们新的变量
		resultValue = lastSample.V
	} else {
		//否则就算差值
		resultValue = lastSample.V - previousSample.V
	}
	//采样时间间隔等于 两个值的时间差
	sampledInterval := lastSample.T - previousSample.T
	fmt.Println("\n\n")
	fmt.Printf("vals %#v\n",vals)
	fmt.Printf("out %#v\n",out)
	fmt.Println("last",lastSample.T)
	fmt.Println("pre",previousSample.T)
	fmt.Println("samp",sampledInterval)
	fmt.Println("len",len(vals[0].(Matrix)[0].Points))
	fmt.Println("\n\n")
	//时间间隔为0 就直接返回out
	if sampledInterval == 0 {
		// Avoid dividing by 0.
		return out
	}
	//这里是irate 传递的是true 详情见funcIrate
	if isRate {
		// Convert to per-second.
		//转换为每s
		// a = float64(sampledInterval) / 1000
		// resultValue = resultValue / a
		//这里sampledInterval 是我们计算的时间差
		resultValue /= float64(sampledInterval) / 1000
	}
	return append(out, Sample{
		Point: Point{V: resultValue},
	})
}

我这里prome配置的采集间隔为20s

irate(go_memstats_alloc_bytes_total[1m])

在这里插入图片描述

irate(go_memstats_alloc_bytes_total[2m])

在这里插入图片描述
根据代码以及promeql不难看出 1m 3个采集点 也就是说20 * 3 ,2m 6个采集点 也就是说20 * 6
根据代码是从采集点中首先判断如果低于2个指标直接返回,如果2个以上取最后两个并且判断大小 如果后续大于上一个 那么就取差值,并且除以采集间隔,算出每秒的数据 counter是只增不减的数据类型

三 rate

func funcRate(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
	return extrapolatedRate(vals, args, enh, true, true)
}
/*
extrapolatedRate是用于速率/增加/增量的实用函数。
它计算速率(如果isCounter为true,则允许计数器重置),
如果第一个/最后一个采样接近边界,则进行推断,
并以每秒(如果isRate为true)或总体形式返回结果。
 */
//rate 传递的参数为extrapolatedRate(vals, args, enh, true, true)
//由此可以理解rate一般用于Counter类型
func extrapolatedRate(vals []Value, args Expressions, enh *EvalNodeHelper, isCounter bool, isRate bool) Vector {
	ms := args[0].(*MatrixSelector)
	/*
	type MatrixSelector struct {
		// 如果解析器未返回错误,则可以假定这是一个VectorSelector。
		VectorSelector Expr
		Range          time.Duration
		EndPos Pos
	}
	type Expr interface {
		Node
		// Type返回表达式求值的类型。 它不会执行深入检查,因为这是在解析时完成的。
		Type() ValueType
		//expr确保没有其他类型的接口意外实现。
		expr()
	}
	type ValueType string
	type Node interface {
		// 当作为有效查询的一部分进行解析时,返回给定节点的节点的字符串表示形式。
		String() string
		//PositionRange返回AST节点在查询字符串中的位置。
		PositionRange() PositionRange
	}
	 */
	vs := ms.VectorSelector.(*VectorSelector)
	/*
	type VectorSelector struct {
		Name          string
		Offset        time.Duration
		LabelMatchers []*labels.Matcher
		//在查询准备时填充未扩展的seriesSet。
		unexpandedSeriesSet storage.SeriesSet
		series              []storage.Series
		PosRange PositionRange
	}
	*/

	var (
		samples    = vals[0].(Matrix)[0]
		//durationMilliseconds
		//d/  (s/ns)
		//int64(d / (time.Millisecond / time.Nanosecond))
		rangeStart = enh.ts - durationMilliseconds(ms.Range+vs.Offset)
		rangeEnd   = enh.ts - durationMilliseconds(vs.Offset)
	)

	// 在没有至少两个点的情况下尝试计算速率没有任何意义。 删除此Vector元素。
	if len(samples.Points) < 2 {
		return enh.out
	}
	var (
		counterCorrection float64
		lastValue         float64
	)
	fmt.Printf("%#v\n",ms)
	fmt.Printf("%#v\n",vs)
	fmt.Printf("%#v\n",samples.Points)
	//这里主要是为了防止不是counter类型的 会出现忽大忽小
	for _, sample := range samples.Points {
		if isCounter && sample.V < lastValue {
			counterCorrection += lastValue
		}
		lastValue = sample.V
	}
	//拿到第一个根最后一个的中间差
	resultValue := lastValue - samples.Points[0].V + counterCorrection

	// Duration between first/last samples and boundary of range.
	//第一个/最后一个样本与范围边界之间的持续时间。
	durationToStart := float64(samples.Points[0].T-rangeStart) / 1000
	durationToEnd := float64(rangeEnd-samples.Points[len(samples.Points)-1].T) / 1000

	sampledInterval := float64(samples.Points[len(samples.Points)-1].T-samples.Points[0].T) / 1000
	averageDurationBetweenSamples := sampledInterval / float64(len(samples.Points)-1)

	if isCounter && resultValue > 0 && samples.Points[0].V >= 0 {
		// Counters cannot be negative. If we have any slope at
		// all (i.e. resultValue went up), we can extrapolate
		// the zero point of the counter. If the duration to the
		// zero point is shorter than the durationToStart, we
		// take the zero point as the start of the series,
		// thereby avoiding extrapolation to negative counter
		// values.
		//计数器不能为负。
		//如果我们有任何斜率(即resultValue上升),我们可以外推计数器的零点。
		//如果到零点的持续时间比durationToStart短,我们将零点作为序列的开始,从而避免外推到负计数器值。
		durationToZero := sampledInterval * (samples.Points[0].V / resultValue)
		if durationToZero < durationToStart {
			durationToStart = durationToZero
		}
	}

	// If the first/last samples are close to the boundaries of the range,
	// extrapolate the result. This is as we expect that another sample
	// will exist given the spacing between samples we've seen thus far,
	// with an allowance for noise.
	//如果第一个/最后一个样本接近范围的边界,则外推结果。
	//就像我们期望的那样,考虑到到目前为止我们看到的样本之间的间隔,还会有另一个样本存在,并且要考虑到噪声。
	extrapolationThreshold := averageDurationBetweenSamples * 1.1
	extrapolateToInterval := sampledInterval

	if durationToStart < extrapolationThreshold {
		extrapolateToInterval += durationToStart
	} else {
		extrapolateToInterval += averageDurationBetweenSamples / 2
	}
	if durationToEnd < extrapolationThreshold {
		extrapolateToInterval += durationToEnd
	} else {
		extrapolateToInterval += averageDurationBetweenSamples / 2
	}
	resultValue = resultValue * (extrapolateToInterval / sampledInterval)
	if isRate {
		resultValue = resultValue / ms.Range.Seconds()
	}

	return append(enh.out, Sample{
		Point: Point{V: resultValue},
	})
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自万古的忧伤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值