数据模型
Prometheus 底层将所有数据存储为工夫序列: 即以雷同 metric 和 label 维度聚合的带工夫戳的流。
时序格局
上面格局示意一个 metric 和 label 的时序数据汇合
<metric name>{<label name>=<label value>, ...}
例如,一个工夫序列的度量名称 api_http_requests_total 和 label 办法="POST" 和handler="/messages"能够写成这样:
api_http_requests_total{method="POST", handler="/messages"}
采样
Prometheus 通过 pull 收到各个 metric 的理论数据,样本造成理论的工夫序列数据,每个样本包含:
- float64值
- 一个毫秒精度工夫戳
小结
- Prometheus 以 metric 和 label 的维度聚合时序数据
- Prometheus 以 pull 形式,收集各个监控指标上 metric 的值与工夫戳
Metric 类型
Prometheus Metric 有四种类型:Counter,Gauge,Histogram,Summary
Counter
- 个性:只增不减
- 实用:服务申请数、已实现工作数、谬误呈现次数等
- 示例:http 申请数
# TYPE prometheus_http_requests_total counterprometheus_http_requests_total{code="200",handler="/-/ready"} 6prometheus_http_requests_total{code="200",handler="/api/v1/label/:name/values"} 43prometheus_http_requests_total{code="200",handler="/api/v1/labels"} 35
- Go SDK:
// counter + 1Inc()// counter + 任意值,这个值小于 0, 则 panicAdd(float64)
Gauge
- 个性:数据能够任意变动,可增可减
- 实用:温度、内存/磁盘使用率等
- 示例:go 以后协程数
# TYPE go_goroutines gaugego_goroutines 35
- Go SDK:
// 设置任意值Set(float64)// 加一Inc()// 减1Dec()// 加任意值Add(float64)// 减任意值Sub(float64)// 设置成以后工夫的 unix 秒值SetToCurrentTime()
Histogram(直方图)
- 个性:一段时间范畴内对数据进行采样,对其指定区间以及总数进行统计
- 实用:页面的响应工夫散布、resp 的 body 大小散布等...
- 示例:http 申请延时
# HELP prometheus_http_request_duration_seconds Histogram of latencies for HTTP requests.# TYPE prometheus_http_request_duration_seconds histogramprometheus_http_request_duration_seconds_bucket{handler="/",le="0.1"} 6 // 延时小于 0.1 s 的申请数 6 个prometheus_http_request_duration_seconds_bucket{handler="/",le="0.2"} 6 // 延时小于 0.2 s 的申请数 6 个prometheus_http_request_duration_seconds_bucket{handler="/",le="0.4"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="1"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="3"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="8"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="20"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="60"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="120"} 6prometheus_http_request_duration_seconds_bucket{handler="/",le="+Inf"} 6 // 这个实际上等于总申请数prometheus_http_request_duration_seconds_sum{handler="/"} 0.00016767900000000003 // 上述样本求和 sumprometheus_http_request_duration_seconds_count{handler="/"} 6 // 这次采样总申请数
- Go SDK :
// 观测这个值落在了哪个 buckte 中Observe(float64)
Summary
- 个性:与 Histogram 相似
- 实用:与 Histogram 相似
- 示例:垃圾回收进展工夫
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.# TYPE go_gc_duration_seconds summarygo_gc_duration_seconds{quantile="0"} 4.2354e-05go_gc_duration_seconds{quantile="0.25"} 9.222e-05go_gc_duration_seconds{quantile="0.5"} 0.000133648go_gc_duration_seconds{quantile="0.75"} 0.000193116go_gc_duration_seconds{quantile="1"} 0.00906632go_gc_duration_seconds_sum 0.064656275go_gc_duration_seconds_count 295
- Go SDK:
// 观测值Observe(float64)
Summary VS Histogram
雷同
- 都用来示意一段时间内数据采样后果
- 实用于查看值散布的场景
不同点
- Histogram:客户端在收集数据时,不存储具体的值,还是多个 bucket counter,性能绝对 Counter,Gauge 无差别。但能够在服务端计算出分位数据
- Summary: 客户端把一段时间内(默认是10分钟)的数据存储下来,再去计算分位数,性能绝对低一些
如何抉择
- 须要聚合,选 Histogram。Summary 无奈聚合,因为个别要监控多台机器,而 Summary 在客户端计算分位数据
- 分位数据要准确,选 Summary
PormQL 应用
PormQL 查问返回数据类型
- 刹时向量(Instant vector): 蕴含一组时序数据,默认返回最新工夫点的值
查问语句,格局与上述提到的时序格局统一,例如
// 例子1 查问 http 接口申请数http_requests_total// 例子2 查问 http 接口申请数,限定 hanlderhttp_requests_total{handler="/api/comments"}
- 返回后果
- 范畴向量(Range vector): 一组时序数据,每个时序有多个值(限定在一段时间内,不同工夫点的值)
- 查问语句,
// 例子1 查问 http 5 分钟内 上报数据http_requests_total[5m]// 例子2 查问 http 指定 handler 5 分钟内 上报的数据http_requests_total{handler="/api/comments"}[5m]
返回后果
- 标量
单纯的数字:2,2.0
查问语句
metric_name{label_name=lable_value}
查问出刹时向量,例如
http_requests_total{code="200"} // 示意查问 metric 为 http_requests_total,label code 为 "200" 的数据
- 刹时查问语句后加上
[time]
,例如metric_name{label_name=lable_value}[5m]
示意限定 5 分钟内的数据,返回的是范畴向量 - 查问条件反对不等于
!=
,正则=~
!~
, 例如
http_requests_total{code!="200"} // 示意查问 code 不为 "200" 的数据http_requests_total{code=~"2.."} // 示意查问 code 为 "2xx" 的数据http_requests_total{code!~"2.."} // 示意查问 code 不为 "2xx" 的数据
- 刹时向量后果反对运算,
+,-,*,/,%,^,==,!=,>,<,>=,<=,and,or,unless,sum,min,max,avg,stddev,stdvar,count,count_values,bottomk,topk,quantile
http_requests_total{code="200"} * 2 // 把外面的值 * 2http_requests_total{code="200"} >= 2 // 只有值 > 2 的数据http_requests_total{code="200"} >= 2 or http_requests_total{code="200"} == 0 // 只有值 > 2 的数据和值为 0 的sum(http_requests_total{code="200"}) // 各个时序数据里的值求和topk(5, http_requests_total{code="200"}) // 只有值在前 5 的时序数据
- 内置函数 如
abs,floor, rate
, 不同函数有着不同的操作对象,例如rate
针对与范畴向量,刹时向量没有增长率
floor(avg(http_requests_total{code="200"})) // 向下取整ceil(avg(http_requests_total{code="200"})) // 向上取整rate(http_requests_total[5m]) // 求 5m 增长率
Go Client 实现自定义 exporter
在上一篇文章中,提到了 Prometheus 提供了许多插件式的 Exporter, 能够通过装置这些 exporter 来监控各种指标,如机器、容器、中间件、MySQL ...
然而如果咱们想监控应用程序呢,如 http,rpc 接口的 QPS、接口时延、错误率。这时候咱们能够利用 Prometheus Client SDK 自定义监控
示例
- 如上面代码所示,有一个 web 的 demo,别离有两个接口
/api1
与api2
package mainimport ( "fmt" "net/http")func main() { http.HandleFunc("/api1", api1) http.HandleFunc("/api2", api2) err := http.ListenAndServe(":1234", nil) if err != nil { panic(err) }}func api1(w http.ResponseWriter, r *http.Request) { fmt.Println("hello api1")}func api2(w http.ResponseWriter, r *http.Request) { fmt.Println("hello api2")}
- 如何利用 prometheus 监控这两个接口申请量与接口时延呢
go get github.com/prometheus/client_golang/prometheus
- 向外裸露 exporter 地址,下述例子应用默认的 exporter 提供了一些 go 程序的根本监控,如 go 协程数,垃圾回收工夫..
http.Handle("/metrics", promhttp.Handler())go func() { _ = http.ListenAndServe(":1235", nil)}()
exporter 上报数据示例如下:
# HELP go_goroutines Number of goroutines that currently exist.# TYPE go_goroutines gaugego_goroutines 9# HELP go_info Information about the Go environment.# TYPE go_info gaugego_info{version="go1.17.2"} 1...
注册自定义接口 Counter
NewCounter
:一个 CounterNewCounterVec
: 一组 Counter,按外面的 label 值分组应用示例如下(残缺例子在这节最初)
// 申明一个 Countervar apiCounterVec = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "http_api_counter", Help: "web http requests number",}, []string{"handler"})// 申明 一组 Countervar apiTotalCounter = prometheus.NewCounter(prometheus.CounterOpts{ Name: "http_api_total_counter", Help: "web http requests number",})// 注册到 prometheus exporter 中prometheus.MustRegister(apiTotalCounter)prometheus.MustRegister(apiCounterVec)// 计数apiTotalCounter.Inc()apiCounterVec.WithLabelValues("/api").Inc()
- 注册自定义接口 Histogram, 示例如下
// 申明一组 Histogramvar apiHandleMS = prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "http_api_handler_ms", Help: "Microseconds of HTTP interface processing", Buckets: prometheus.LinearBuckets(1,19,10),}, []string{"handler"})// 注册到 prometheus exporter 种 prometheus.MustRegister(apiHandleMS)// 计数apiHandleMS.WithLabelValues("/api").Observe(xx)
- 残缺示例仓库, 大抵流程如下
func main() { // 初始化 prometheus exporter initPrometheus() // web 示例 http.HandleFunc("/api1", prometheusMetric(http.HandlerFunc(api1))) http.HandleFunc("/api2", prometheusMetric(http.HandlerFunc(api2))) err := http.ListenAndServe(":1234", nil) if err != nil { panic(err) }}func initPrometheus() { // 注册自定义的监控指标 prometheus.MustRegister(apiTotalCounter) prometheus.MustRegister(apiCounterVec) prometheus.MustRegister(apiHandleMS) // 裸露 exporter 地址,prometheus server 通过 pull 这个地址,拉取指标数据 http.Handle("/metrics", promhttp.Handler()) go func() { _ = http.ListenAndServe(":1235", nil) }()}// metric 中间件func prometheusMetric(handler http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // 总接口访问量计数 apiTotalCounter.Inc() // 单个接口访问量计数 apiCounterVec.WithLabelValues(r.URL.String()).Inc() start := time.Now() handler.ServeHTTP(w, r) ms := time.Now().UnixMicro() - start.UnixMicro() // 接口时延计数 apiHandleMS.WithLabelValues(r.URL.String()).Observe(float64(ms)) }}
自定义指标后果:
总结
- 介绍了 Prometheus 四种 Metric 类型
- 介绍了 PormQL 简略应用
- 以一个理论例子介绍了如何用 Prometheus 提供的 client SDK 自定义监控