3.1、简介
之前的文章分别介绍了prometheuss是如何采集数据以及如何对采集目标做服务发现的,本篇文章分析prometheus是如何将采集后的数据做保存的。
一、Prometheus源码分析之:数据采集) 描述了通过scrapeLoop的run方法采集指标数据。之后scrapeLoop调用append进行指标的保存。
3.2、scrapeCache
真正存储指标的是storage.Appender,在scrape与storage之间有一层缓存。缓存主要的作用是过滤错误的指标。
1 | type scrapeCache struct { |
创建scrapeCache,调用newScrapeLoop,初始化scrapeLoop,会判断scrapeCache是否为空,如果为nil,调用newScrapeCache对cache进行初始化。
1 | if cache == nil { |
newScrapeCache()如下:
1 | func newScrapeCache() *scrapeCache { |
scrapeCache 方法介绍,这里简介各个fun的作用,详细代码不做注解。
1 | // 根据met信息,获取对应的cacheEntry |
3.3、append
分析scrapeLoop.append是如何实现的。
1 | func (sl *scrapeLoop) append(b []byte, contentType string, ts time.Time) (total, added, seriesAdded int, err error) { |
3.4、总结
整个存储逻辑都围绕着过滤无效指标进行。特殊点在于存储的时候指标分为有时间戳与无时间戳两种情况。
(1)有时间戳:
- 解析指标数据通过Series()
- 利用getDropped判断指标是否有效,无效则跳出处理
- 通过get查找对应cacheEntry,如果找到利用app.AddFast直接存储样本值。如果未找到,使用sampleMutator进行解析重置,判断lset是否为空,为空则使用addDropped添加到无效字典中,跳出当前处理,如果有效则使用app.Add存储指标。(可以看到,通过get找到使用AddFast存储,未找到使用Add存储,感兴趣可以看下两个fun实现的区别)
- 通过forEachStale检查指标是否过期。
- app.Add标记过期指标
- 调用iterDone进行相关缓存清理。
(2)无时间戳:
- 每次存储后,如果不带时间戳都会调用trackStaleness,存储指标到seriesCur中
这里seriesCur与seriesPrev的作用就是处理指标label是否过期的。forEachStale实现如下:
1 | func (c *scrapeCache) forEachStale(f func(labels.Labels) bool) { |
如果seriesPrev中的指标(label)存在于seriesPrev,则不处理,如果不存在,则说明过期。其中在iterDone中。
1 | // 交换seriesPrev与seriesCur |
所以,每次存储处理后,都会交换seriesPrev与seriesCur,然后清空seriesCur。下次存储在做比较。如果命中过期规则,则标记该样本值为StaleNaN。