36

玩转PROMETHEUS(6) 实现自定义的Collector

 3 years ago
source link: http://vearne.cc/archives/39380
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.
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://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

打赏我

ZzaQjmz.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK