【轉】玩轉PROMETHEUS(6) 實現自定義的COLLECTOR
原文:http://vearne.cc/archives/39380
-------------------------
版權宣告 本站原創文章 由 萌叔 發表
轉載請註明 萌叔 | 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. 指標採集的觸發時機
Counter
和Histogram
的觸發時機比較明確
比如: 為了監控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-定時任務
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實現
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。