最近对服务进行监控,而当前监控最流行的数据库就是 Prometheus
,同时 go-zero
默认接入也是这款数据库。今天就对 go-zero
是如何接入 Prometheus
,以及开发者如何本身定义本身监控指标。html
go-zero
框架中集成了基于 prometheus
的服务指标监控。可是没有显式打开,须要开发者在 config.yaml
中配置:git
Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics
若是开发者是在本地搭建 Prometheus
,须要在 Prometheus
的配置文件 prometheus.yaml
中写入须要收集服务监控信息的配置:github
- 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
docker run \ -p 9090:9090 \ -v dockeryml/docker-prometheus:/etc/prometheus \ prom/prometheus
打开 localhost:9090
就能够看到:shell
点击 http://service-ip:9091/metrics
就能够看到该服务的监控信息:数据库
上图咱们能够看出有两种 bucket
,以及 count/sum
指标。api
那 go-zero
是如何集成监控指标?监控的又是什么指标?咱们如何定义咱们本身的指标?下面就来解释这些问题app
> 以上的基本接入,能够参看咱们的另一篇:https://zeromicro.github.io/go-zero/service-monitor.html框架
上面例子中的请求方式是 HTTP
,也就是在请求服务端时,监控指标数据不断被搜集。很容易想到是 中间件 的功能,具体代码:https://github.com/tal-tech/go-zero/blob/master/rest/handler/prometheushandler.go。ide
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种数据进行统计: > > go > // 基本上在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
来完成。