我有一个 Go 程序,它获取 Prometheus 时间序列以在其上动态注入标签,然后将它们暴露回“/metrics”以供 Prometheus 再次抓取。
我现在遇到的问题是,每当指标停止报告值时,该指标不会被删除,而是会继续使用与它具有的相同的最后一个值。
示例: 我有 10:00 的时间序列
new_billing_raw:memory_allocated:sum{billed_namespace="monitoring",exported_job="prometheus",task="prometheus",task_group="monitoring",uuaa="CD34"} 300
如果在 10:01 我停止 Prometheus 从中接收这些标签和值的导出器,则该指标的值将为空。 我的程序将无限期地报告最后一个值(本例中为 300),而不是 null,直到我重新启动程序为止。
知道如何解决这个问题吗?
在这种情况下,您不应使用内置指标实现(prometheus.NewGauge 等)。它们永远不会忘记标签值,并且无法取消注册。相反,编写一个实现 prometheus.Collector 的新类型并注册该类型的实例。将查询 prometheus 的代码移至该新类型的 Collect 方法中。
来自文档:
在为导出器实现收集器时,您不应该使用通常的直接检测方法,然后更新每次抓取的指标。
而是每次都创建新的指标。在 Go 中,这是通过 Collect() 方法中的 MustNewConstMetric 完成的。 [...]
造成这种情况的原因有两个。首先,两次刮擦可能同时发生,并且直接检测使用有效的文件级全局变量,因此您将获得竞争条件。其次,如果标签值消失,它仍然会被导出。
这是一个草稿:
package main
import (
"github.com/prometheus/client_golang/prometheus"
)
func main() {
prometheus.MustRegister(&MyCollector{})
}
var memAllocatedDesc = prometheus.NewDesc(
"new_billing_raw:memory_allocated:sum",
"TODO: help",
[]string{"billed_namespace", "exported_job", "task", "task_group", "uuaa"},
nil, // constant labels
)
// add descriptions for more metrics as necessary
type MyCollector struct{}
// Describe implements prometheus.Collector. It sends to ch descriptions of
// all metrics that Collect could ever possibly produce over c's entire
// lifetime.
//
// See also prometheus.DescribeByCollect for a possible shortcut
// implementation.
func (c *MyCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- memAllocatedDesc
}
func (c *MyCollector) Collect(ch chan<- prometheus.Metric) {
// TODO: query upstream prometheus
ch <- prometheus.MustNewConstMetric(
memAllocatedDesc,
prometheus.GaugeValue,
300, // value
"default", // billed_namespace
"code-server", // exported_job
"code-serer", // task
"code-serer", // task_group
"GBT5", // uuaa
)
// Repeat as necessary, for each metric and label set.
}
Describe 在收集器注册时被调用一次。对 /metrics 的每个请求都会调用 Collect。
在 Collect 中查询上游 Prometheus 可确保每次抓取都看到最新值。如果在每次抓取时查询上游 Prometheus 实例的成本太高(不太可能),请异步执行查询并将它们存储在某种并发安全的数据结构中,然后在 Collect 中使用此数据结构。这里的关键是只有发送到
chan prometheus.Metric
的指标才会出现在抓取响应中。