1. 程式人生 > 其它 >【轉】玩轉PROMETHEUS(6) 實現自定義的COLLECTOR

【轉】玩轉PROMETHEUS(6) 實現自定義的COLLECTOR

原文:http://vearne.cc/archives/39380

-------------------------

版權宣告 本站原創文章 由 萌叔 發表
轉載請註明 萌叔 | http://vearne.cc

1. 前言

prometheus的官方和社群為了我們提供了豐富的exporter。對常見的硬體裝置、資料庫、訊息佇列以及常見的軟體進行監控。另外官方還為我們提供了4種指標型別方便我們自定義exporter

  • CounterCounter代表累計指標,它表示單調遞增的計數器,通常用於表示服務請求數,完成的任務數,錯誤的數量。
  • GaugeGauge表示某種瞬時狀態,某一時刻的記憶體使用率、訊息佇列中的訊息數量等等。它的值既可以增大,也可以減小。
  • 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