Fork me on GitHub

版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc

1. 前言

prometheus的官方和社区为了我们提供了丰富的exporter。对常见的硬件设备、数据库、消息队列以及常见的软件进行监控。另外官方还为我们提供了4种指标类型方便我们自定义exporter

  • Counter Counter代表累计指标,它表示单调递增的计数器,通常用于表示服务请求数,完成的任务数,错误的数量。
  • Gauge Gauge表示某种瞬时状态,某一时刻的内存使用率、消息队列中的消息数量等等。它的值既可以增大,也可以减小。
  • Histogram 通常用于top percentile,比如请求耗时的TP90、TP99等
  • Summary 类似于Histogram

我们回顾一下prometheus的指标采集的一般过程

1) 创建指标

HTTPReqTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total number of HTTP requests made.",
    }, []string{"method", "path", "status"})

2) 指标注册到 DefaultRegisterer

 prometheus.MustRegister(
        HTTPReqTotal,
    )

3) 指标和对应的值通过HTTP API暴露出来

The caller of the Gather method can then expose the gathered metrics in some way. Usually, the metrics are served via HTTP on the /metrics endpoint.

http.Handle("/metrics", promhttp.Handler())

当promhttp.Handler()被执行时,所有metric被序列化输出。题外话,其实输出的格式既可以是plain text,也可以是protocol Buffers。

这里涉及一个问题,这些metric的值是什么时候采集的?

2. 指标采集的触发时机

CounterHistogram的触发时机比较明确
比如: 为了监控QPS, 请求完成以后,请求数量直接+1

        HTTPReqTotal.With(prometheus.Labels{
            PromLabelMethod: "GET",
            PromLabelPath:   "/api/query",
            PromLabelStatus: "200",
        }).Inc()

比如: 为了监控请求耗时分布,请求完成以后,只需

HTTPReqDuration.With(prometheus.Labels{
            PromLabelMethod: "GET",
            PromLabelPath:   "/api/query",
        }).Observe(3.15)

对于 Gauge这种瞬时值,该怎么办?
假定我们要监控某个channel的长度

var jobChannel chan int

2.1 方法1-定时任务

example1.go

jobChanLength:= prometheus.NewGauge(prometheus.GaugeOpts{
        Name: "job_channel_length",
        Help: "length of job channel",
    })
    go func() {
        jobChanLength.Set(float64(len(jobChannel)))
        c := time.Tick(30 * time.Second)
        for range c {
            jobChanLength.Set(float64(len(jobChannel)))
        }
    }()

2.2 方法2-自定义Collector

example2.go
实现Collector接口

type Collector interface {
    Describe(chan<- *Desc)
    // 比如满足并发安全
    Collect(chan<- Metric) 
}

Collect is called by the Prometheus registry when collecting metrics

当promhttp.Handler()被调用是,Collect函数被执行,所以此函数不能运行时间过长,否则可能导致promhttp.Handler()执行超时。

type ChanCollector struct{
    JobChannel chan int
    Name string
}

func NewChanCollector(jobChannel chan int, name string) *ChanCollector{
    c := ChanCollector{}
    c.JobChannel = jobChannel
    c.Name = name
    return &c
}

func (c *ChanCollector) Describe(ch chan<- *prometheus.Desc) {
    desc := prometheus.NewDesc(c.Name, "length of channel", nil, nil)
    ch <- desc
}

func (c *ChanCollector) Collect(ch chan<- prometheus.Metric) {
    ch <- prometheus.MustNewConstMetric(
        prometheus.NewDesc(c.Name, "length of channel", nil, nil),
        prometheus.GaugeValue,
        float64(len(c.JobChannel)),
    )
}
    collector := NewChanCollector(jobChannel, "job_channel_length")
    prometheus.MustRegister(collector)

2.3 方法3-利用GaugeFunc实现

example3.go

    jobChanLength := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
        Name: "job_channel_length",
        Help: "length of job channel",
    },func() float64{
        return float64(len(jobChannel))
    })

    prometheus.MustRegister(jobChanLength)

只需要定义函数 func()float64{} 即可
当promhttp.Handler()被调用时,func()float64{} 被触发。

3. 总结

如果执行速度很快,直接使用方法3就可以; 但是如果执行速度很快且输出的指标很多,推荐使用方法2; 其它情况推荐使用方法1。

参考资料

  1. EXPORTERS AND INTEGRATIONS
  2. METRIC TYPES

微信公众号

4 对 “玩转PROMETHEUS(6) 实现自定义的Collector”的想法;

  1. func (c *ChanCollector) Collect(ch chan<- prometheus.Metric) 应该是
    func (c *ChanCollector) Collect(ch chan<- *prometheus.Metric)

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据