簡述
PrometheUS有四種數據類型Counter、Gauge、Histogram、Histogram。當我讀官方文檔的時候,前兩種數據類型一讀就明白了,可是後兩種就讓人難受了,怎麼讀也整不明白了,哎,頭皮發麻呀,今天抽空折騰一下
Histogram
在有道翻譯中這是柱狀圖的意思。舉個栗子:我想獲取北京最近十天內,溫度分佈情況,我們可以用Histogram這個數據類型,如果用圖像表示,也就是柱形圖比較合適了,如下圖橫軸是溫度,豎軸是天數
當我們配置了Histogram數據類型後,我們能得到:
- 每個桶中累積值的數量。這裏的“桶”就是上面橫軸的數值(5,10,15,20),而累計值不是上面的豎軸的值,而是一個累積計數,上圖中5的桶對應的累積值是2,10的桶對應的累積值是4,15的桶對應的累積值對應的是8,20的桶對應的是10
- 所有樣本值的總和,比如上圖中在形成柱狀圖之前,我們的數據應該是10天的溫度(假設溫度只在5,10,15,20中取值),然後統計出對應溫度的天數而形成柱狀圖。此處的總和就是10天溫度的總和
- 樣本數量,對應上圖就是10了
Summary
這種類型類似於Histogram,他們都提供了樣本值的總和和樣本數量。但是Summary沒有提供每個桶中的累積值,而是提供了一個 φ-quantiles。
- φ-quantiles
說一下 φ-quantiles吧。 φ-quantiles中的φ的取值範圍是0 ≤ φ ≤ 1,總體的意思就是,如果你有N個樣本值,首先要從小到大排序,然後取出排在φ *N的值。 - histogram_quantile
這是Histogram類型計算φ-quantiles時用的函數。這個函數的邏輯是,(1)最高的桶的上線如果不是+Inf,那麼返回NaN (2)如果quantile 落在最高的桶,那麼會返回最高桶的下線值 (3)如果最低桶的上限是大於0的,那麼就會使用插值的方式。如果最低同的上限小於等於0,那麼就會返回最低桶的上限 - Quantile error
我覺得可以翻譯成誤差,對於Histogram來說,比如我們有一個200ms 到 300ms的桶,我們的值都接近於220ms,所以0.95-quantiles應該接近於220ms,但是實際上,PrometheUS會使用插值的方法計算φ-quantiles,那麼取值就會是295ms
而對於Summary來說,上面的情況不會出現。但是Summary在定義φ-quantiles的時候,會定義一個波動範圍,那麼實際上φ-quantiles的計算會在φ加減波動範圍內波動,如果這個範圍內值的波動範圍很大,那麼取值就會不準確了
Summary和Histogram的不同
Histogram | Summary | |
---|---|---|
能獲取到的值 | 可以獲取到每個桶的累積值 | 能獲取到φ-quantiles,但是φ是在客戶端寫死的,比如φ在客戶端時0.5,那麼通過API就只能獲取0.5-quantiles |
φ-quantiles的計算 | 需要在server端通過histogram_quantile()函數實現 | 客戶端直接計算 |
客戶端性能 | 優於Summary | 因爲要計算φ-quantiles,所以性能差了點 |
Quantile error避免方式 | 需要控制桶之間的間隔 | 需要控制φ的範圍,在summary定義φ時會同時定一個波動範圍,比如φ爲0.95波動範圍是0.01那麼0.95-quantiles會在0.96-quantiles和0.94-quantiles取值 |
場景 | 如果你的φ-quantiles中φ不能定死,那麼就要使用Histogram類型。如果你對要對一部分數據進行計算,那麼要使用這種類型 |
實現
我用go寫了個小demon:
- 目錄結構
- histogram.go
package histogram
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
)
var (
TemperatureHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "beijing_temperature",
Help: "The temperature of the beijing",
Buckets: prometheus.LinearBuckets(0,10,3),
})
)
func InsertTemperature(){
var temperature = [10]float64{1,4,5,10,14,15,20,25,11,30}
for i:=0;i<len(temperature);i++{
TemperatureHistogram.Observe(temperature[i])
fmt.Printf("insert number: %f \n", temperature[i])
}
}
- summary
package summary
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
)
var (
SalarySummary = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "beijing_salary",
Help: "the relationship between salary and population of beijing city",
Objectives: map[float64]float64{0.5:0.05,0.8:0.001,0.9:0.01,0.95:0.01},
})
)
func InsertSummary(){
var salary = [10]float64{8000,7000,8900,10000,9800,17000,15000,14000,11000,12000}
for i:=0;i<len(salary);i++{
SalarySummary.Observe(salary[i])
fmt.Printf("Insert number: %f \n", salary[i])
}
}
- main.go
package main
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"test_histograms/src/histogram"
"test_histograms/src/summary"
"log"
"net/http"
)
func init() {
prometheus.MustRegister(histogram.TemperatureHistogram)
prometheus.MustRegister(summary.SalarySummary)
}
func main() {
histogram.InsertTemperature()
summary.InsertSummary()
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080",nil))
}
運行,訪問測試
先寫的這裏了,如果有問題或者異議,請進QQ羣630300475,大家聊聊