最近對服務進行監控,而當前監控最流行的資料庫就是 Prometheus,同時 go-zero 默認接入也是這款資料庫,今天就對 go-zero 是如何接入 Prometheus ,以及開發者如何自己定義自己監控指標,
監控接入
go-zero 框架中集成了基于 prometheus 的服務指標監控,但是沒有顯式打開,需要開發者在 config.yaml 中配置:
Prometheus:
Host: 127.0.0.1
Port: 9091
Path: /metrics
如果開發者是在本地搭建 Prometheus,需要在 Prometheus 的組態檔 prometheus.yaml 中寫入需要收集服務監控資訊的配置:
- job_name: 'file_ds'
static_configs:
- targets: ['your-local-ip:9091']
labels:
job: activeuser
app: activeuser-api
env: dev
instance: your-local-ip:service-port
因為本地是用 docker 運行的,將 prometheus.yaml 放置在 docker-prometheus 目錄下:
docker run \
-p 9090:9090 \
-v dockeryml/docker-prometheus:/etc/prometheus \
prom/prometheus
打開 localhost:9090 就可以看到:

點擊 http://service-ip:9091/metrics 就可以看到該服務的監控資訊:

上圖我們可以看出有兩種 bucket,以及 count/sum 指標,
那 go-zero 是如何集成監控指標?監控的又是什么指標?我們如何定義我們自己的指標?下面就來解釋這些問題
以上的基本接入,可以參看我們的另外一篇:https://zeromicro.github.io/go-zero/service-monitor.html
如何集成
上面例子中的請求方式是 HTTP,也就是在請求服務端時,監控指標資料不斷被搜集,很容易想到是 中間件 的功能,具體代碼:https://github.com/tal-tech/go-zero/blob/master/rest/handler/prometheushandler.go,
var (
metricServerReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{
...
// 監控指標
Labels: []string{"path"},
// 直方圖分布中,統計的桶
Buckets: []float64{5, 10, 25, 50, 100, 250, 500, 1000},
})
metricServerReqCodeTotal = metric.NewCounterVec(&metric.CounterVecOpts{
...
// 監控指標:直接在記錄指標 incr() 即可
Labels: []string{"path", "code"},
})
)
func PromethousHandler(path string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 請求進入的時間
startTime := timex.Now()
cw := &security.WithCodeResponseWriter{Writer: w}
defer func() {
// 請求回傳的時間
metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), path)
metricServerReqCodeTotal.Inc(path, strconv.Itoa(cw.Code))
}()
// 中間件放行,執行完后續中間件和業務邏輯,重新回到這,做一個完整請求的指標上報
// [??:洋蔥模型]
next.ServeHTTP(cw, r)
})
}
}
其實整個很簡單:
HistogramVec負責請求耗時搜集:bucket存放的就是option指定的耗時指標,某個請求耗時多少就會被聚集對應的桶,計數,- 最終展示的就是一個路由在不同耗時的分布,很直觀提供給開發者可以優化的區域,
CounterVec負責指定labels標簽搜集:Labels: []string{"path", "code"}labels相當一個tuple,go-zero是以(path, code)作為整體,記錄不同路由不同狀態碼的回傳次數,如果4xx,5xx過多的時候,是不是應該看看你的服務健康程度?
如何自定義
go-zero 中也提供了 prometheus metric 基本封裝,供開發者自己開發自己 prometheus 中間件,
代碼:https://github.com/tal-tech/go-zero/tree/master/core/metric
| 名稱 | 用途 | 搜集函式 |
|---|---|---|
| CounterVec | 單一的計數,用做:QPS統計 | CounterVec.Inc() 指標+1 |
| GuageVec | 單純指標記錄,適用于磁盤容量,CPU/Mem使用率(可增加可減少) | GuageVec.Inc()/GuageVec.Add() 指標+1/指標加N,也可以為負數 |
| HistogramVec | 反應數值的分布情況,適用于:請求耗時、回應大小 | HistogramVec.Observe(val, labels) 記錄指標當前對應值,并找到值所在的桶,+1 |
另外對
HistogramVec.Observe()做一個基本分析:我們其實可以看到上圖每個
HistogramVec統計都會有3個序列出現:
_count:資料個數_sum:全部資料加和_bucket{le=a1}:處于[-inf, a1]的資料個數所以我們也猜測在統計程序中,分3種資料進行統計:
// 基本上在prometheus的統計都是使用 atomic CAS 方式進行計數的 // 性能要比使用 Mutex 要高 func (h *histogram) observe(v float64, bucket int) { n := atomic.AddUint64(&h.countAndHotIdx, 1) hotCounts := h.counts[n>>63] if bucket < len(h.upperBounds) { // val 對應資料桶 +1 atomic.AddUint64(&hotCounts.buckets[bucket], 1) } for { oldBits := atomic.LoadUint64(&hotCounts.sumBits) newBits := math.Float64bits(math.Float64frombits(oldBits) + v) // sum指標數值 +v(畢竟是總數sum) if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) { break } } // count 統計 +1 atomic.AddUint64(&hotCounts.count, 1) }
所以開發者想定義自己的監控指標:
- 在使用
goctl生成API代碼指定要生成的 中間件:https://zeromicro.github.io/go-zero/middleware.html - 在中間件檔案書寫自己需要統計的指標邏輯
- 當然,開發者也可以在業務邏輯中書寫統計的指標邏輯,同上,
上述都是針對 HTTP 部分邏輯的決議,RPC 部分的邏輯類似,你可以在 攔截器 部分看到設計,
總結
本文分析了 go-zero 服務監控指標的邏輯,當然對于一些基礎設施的監控,prometheus 可以通過引入對應的 exporter 來完成,
專案地址
https://github.com/tal-tech/go-zero
歡迎使用 go-zero 并 star 支持我們!
go-zero 系列文章見『微服務實踐』公眾號
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/265042.html
標籤:其他
