版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | 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

微信公众号