Go 進階: Prometheus Client 教程
【導讀】prometheus 監控 go 程序要怎麼做?數據格式和上報都是什麼樣的?本文做了詳細介紹。
- Prometheus 是什麼? ==================
Prometheus 是一個開源監控系統. Prometheus 是由 SoundCloud 開發的開源監控報警系統和時序列數據庫 (TSDB).Prometheus 使用 Go 語言開發, 是 Google BorgMon 監控系統的開源版本.
1.1 Prometheus 的優勢
-
由指標名稱和和鍵 / 值對標籤標識的時間序列數據組成的多維數據模型.
-
強大的查詢語言 PromQL.
-
不依賴分佈式存儲; 單個服務節點具有自治能力.
-
時間序列數據是服務端通過 HTTP 協議主動拉取獲得的.
-
也可以通過中間網關來推送時間序列數據.
-
可以通過靜態配置文件或服務發現來獲取監控目標.
-
支持多種類型的圖表和儀表盤.
1.2 Prometheus 的組件
Prometheus 生態系統由多個組件組成, 其中大多數組件都是用 Go 編寫的, 因此很容易構建和部署爲靜態二進制文件, 其中有許多組件是可選的:
-
Prometheus Server 作爲服務端, 用來存儲時間序列數據.
-
客戶端庫用來檢測應用程序代碼.
-
用於支持臨時任務的推送網關.
-
Exporter 用來監控 HAProxy,StatsD,Graphite 等特殊的監控目標, 並向 Prometheus 提供標準格式的監控樣本數據.
-
alartmanager 用來處理告警.
-
其他各種周邊工具.
-
其實 Prometheus 也是一種時序數據庫.
- 什麼是 TSDB? ============
TSDB(Time Series Database) 時序列數據庫, 我們可以簡單的理解爲一個優化後用來處理時間序列數據的軟件, 並且數據中的數組是由時間進行索引的.
2.1 TSDB 時間序列數據庫數據寫入的特點
-
寫入平穩, 持續, 高併發高吞吐:時序數據的寫入是比較平穩的, 這點與應用數據不同, 應用數據通常與應用的訪問量成正比, 而應用的訪問量通常存在波峯波谷. 時序數據的產生通常是以一個固定的時間頻率產生, 不會受其他因素的制約, 其數據生成的速度是相對比較平穩的.
-
寫多讀少:時序數據上 95%-99% 的操作都是寫操作, 是典型的寫多讀少的數據. 這與其數據特性相關, 例如監控數據, 您的監控項可能很多, 但是您真正去讀的可能比較少, 通常只會關心幾個特定的關鍵指標或者在特定的場景下才會去讀數據.
-
實時寫入最近生成的數據, 無更新:時序數據的寫入是實時的, 且每次寫入都是最近生成的數據, 這與其數據生成的特點相關, 因爲其數據生成是隨着時間推進的, 而新生成的數據會實時的進行寫入. 數據寫入無更新, 在時間這個維度上, 隨着時間的推進, 每次數據都是新數據, 不會存在舊數據的更新, 不過不排除人爲的對數據做訂正.
2.2 TSDB 時間序列數據庫數據查詢和分析的特點
按時間範圍讀取:通常來說, 您不會去關心某個特定點的數據, 而是一段時間的數據.
-
最近的數據被讀取的概率高
-
歷史數據粗粒度查詢的概率搞
-
多種精度查詢
-
多維度分析
-
數據存儲的特點
接下來的例子中將主要介紹把 Prometheus 當作時序數據庫 TSDB, 應用到 Go 語言業務 app 中做數據統計分析. 我們將着重介紹 Prometheus 客戶端在 Go 語言中自定義.
- Prometheus 在 Go 語言 app 實踐 ============================
Prometheus 的 Client Library 提供度量的四種基本類型包括: Counter,Gauge,Histogram,Summary.
-
Counter 類型, Counter 類型好比計數器, 用於統計類似於:CPU 時間, API 訪問總次數, 異常發生次數等等場景. Gauge 類型, 英文直譯的話叫 “計量器”, 但是和 Counter 的翻譯太類似了, 因此我個人更喜歡使用” 儀表盤“這個稱呼.
-
Histogram 柱狀圖, 更多的是用於統計一些數據分佈的情況, 用於計算在一定範圍內的分佈情況, 同時還提供了度量指標值的總和.
-
Summary 摘要, 和 Histogram 柱狀圖比較類似, 主要用於計算在一定時間窗口範圍內度量指標對象的總數以及所有對量指標值的總和.
3.1 官方 Golang Prometheus Client Example 代碼註釋詳解
官方 Golang Prometheus Client 業務代碼流程:
-
創建 Prometheus Metric 數據項, 可 export 被其他包可以訪問
-
註冊定義好的 Metric
-
相當於執行 SQL create table 業務在無代碼中想插入對時序數據庫 TSDB 數據的數據寫入操作, 相當於時序 SQL insert. 這部分代碼可以放在自己的其他包的業務邏輯中去
-
提供 HTTP API 接口, 讓 Prometheus 主程序定時來收集 TSDB 數據.
package main
import (
"flag"
"log"
"math"
"math/rand"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
uniformDomain = flag.Float64("uniform.domain", 0.0002, "The domain for the uniform distribution.")
normDomain = flag.Float64("normal.domain", 0.0002, "The domain for the normal distribution.")
normMean = flag.Float64("normal.mean", 0.00001, "The mean for the normal distribution.")
oscillationPeriod = flag.Duration("oscillation-period", 10*time.Minute, "The duration of the rate oscillation period.")
)
var (
// 1. 創建 Prometheus 數據Metric, 就相當於SQL 數據庫 聲明table
// Create a summary to track fictional interservice RPC latencies for three
// distinct services with different latency distributions. These services are
// differentiated via a "service" label.
RpcDurations = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "rpc_durations_seconds",
Help: "RPC latency distributions.這個metric的幫助信息,metric的項目作用說明",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
},
[]string{"service"},
)
// The same as above, but now as a histogram, and only for the normal
// distribution. The buckets are targeted to the parameters of the
// normal distribution, with 20 buckets centered on the mean, each
// half-sigma wide.
RpcDurationsHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "rpc_durations_histogram_seconds",
Help: "RPC latency distributions.這個metric的幫助信息,metric的項目作用說明",
Buckets: prometheus.LinearBuckets(*normMean-5**normDomain, .5**normDomain, 20),
})
)
func init() {
// 2. 註冊定義好的Metric 相當於執行SQL create table 語句
// Register the summary and the histogram with Prometheus's default registry.
prometheus.MustRegister(RpcDurations)
prometheus.MustRegister(RpcDurationsHistogram)
// Add Go module build info.
prometheus.MustRegister(prometheus.NewBuildInfoCollector())
}
func main() {
flag.Parse()
start := time.Now()
oscillationFactor := func() float64 {
return 2 + math.Sin(math.Sin(2*math.Pi*float64(time.Since(start))/float64(*oscillationPeriod)))
}
// 3. 業務在無代碼中想插入對時序書庫TSDB數據想的數據寫入操作,相當與SQL insert
// Periodically record some sample latencies for the three services.
go func() {
for {
v := rand.Float64() * *uniformDomain
RpcDurations.WithLabelValues("uniform").Observe(v)
time.Sleep(time.Duration(100*oscillationFactor()) * time.Millisecond)
}
}()
go func() {
for {
v := (rand.NormFloat64() * *normDomain) + *normMean
RpcDurations.WithLabelValues("normal").Observe(v)
RpcDurationsHistogram.Observe(v)
time.Sleep(time.Duration(75*oscillationFactor()) * time.Millisecond)
}
}()
go func() {
for {
v := rand.ExpFloat64() / 1e6
RpcDurations.WithLabelValues("exponential").Observe(v)
time.Sleep(time.Duration(50*oscillationFactor()) * time.Millisecond)
}
}()
// 4. 提供HTTP API接口,讓Prometheus 主程序定時來收集時序數據
// Expose the registered metrics via HTTP.
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(*addr, nil))
}
3.2 業務代碼示例: Gin App 統計 API 請求數量
這裏有一個 Golang gin HTTP API app 我想統計 API 請求相關信息.
第一步: 初始換監控項 Metric
在我的項目創建 package stat, 初始化 API 請求相關的Metric
. 註冊到github.com/prometheus/client_golang/prometheus stat/prometheus.go
package stat
import (
"github.com/prometheus/client_golang/prometheus"
)
var (
// The same as above, but now as a histogram, and only for the normal
// distribution. The buckets are targeted to the parameters of the
// normal distribution, with 20 buckets centered on the mean, each
// half-sigma wide.
//GaugeVecApiDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
// Name: "HttpDuration",
// Help: "api requset 耗時單位ms",
// Buckets: prometheus.LinearBuckets(0, 1000, 50),
//})
GaugeVecApiDuration = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "apiDuration",
Help: "api耗時單位ms",
}, []string{"WSorAPI"})
GaugeVecApiMethod = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "apiCount",
Help: "各種網絡請求次數",
}, []string{"method"})
GaugeVecApiError = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "apiErrorCount",
Help: "請求api錯誤的次數type: api/ws",
}, []string{"type"})
)
func init() {
// Register the summary and the histogram with Prometheus's default registry.
prometheus.MustRegister(GaugeVecApiMethod, GaugeVecApiDuration, GaugeVecApiError)
}
第二步: 在業務代碼中採集數據
創建mw_prometheus_http.go
, 統計 API 請求持續的實踐和 Method 的計數, 代碼如下
package handler
import (
"fortress/stat"//導入第一步中初始換好的stat(prometheus client_golang 初始化包中的監控項)
"github.com/gin-gonic/gin"
"time"
)
func MwPrometheusHttp(c *gin.Context) {
start := time.Now()
method := c.Request.Method
stat.GaugeVecApiMethod.WithLabelValues(method).Inc()
c.Next()
// after request
end := time.Now()
d := end.Sub(start) / time.Millisecond
stat.GaugeVecApiDuration.WithLabelValues(method).Set(float64(d))
}
統計 API 錯誤返回的數量stat.GaugeVecApiError.WithLabelValues("API").Inc()
inc
來記錄 API 發送的現錯誤
handler/helper.go
import (
"fortress/stat"
)
func jsonError(c *gin.Context, msg interface{}) {
stat.GaugeVecApiError.WithLabelValues("API").Inc()
var ms string
switch v := msg.(type) {
case string:
ms = v
case error:
ms = v.Error()
default:
ms = ""
}
c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": ms})
}
大家可以舉一反三.
第三步: gin app 提供/metric
接口給 prometheus TSDB 時序數據庫收集數據
提供 metrics 接口給 prometheus TSDB 時序數據庫收集數據 r.GET("metrics", gin.WrapH(promhttp.Handler()))
使用第一步定義的中間件統計 API 接口信息:r.GET("metrics", gin.WrapH(promhttp.Handler()))
...
r.GET("metrics", gin.WrapH(promhttp.Handler()))
api := r.Group("api").Use(handler.MwPrometheusHttp)
...
第四步: Prometheus 主程序 Pull 數據.
最後一步, 在 Prometheus 主程序的配置文件填寫第三步的 API 接口信息, Prometheus TSDB 時序數據庫開始定時收集 metric 數據
第五步: 數據展示
Prometheus 提供了一種功能表達式語言 PromQL, 允許用戶實時選擇和匯聚時間序列數據. 表達式的結果可以在瀏覽器中顯示爲圖形, 也可以顯示爲表格數據, 或者由外部系統通過 HTTP API 調用.
當然您可以參照這個這篇文章: 使用 Prometheus 和 Grafana 做可視化大屏展示做數據展示.
轉自:EricZhou
mojotv.cn/go/prometheus-client-for-go
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Vj_4CQUucLN-MzCbPQ6gXA