浅谈prometheus(普罗米修斯) client golang
浅谈prometheus client golang
含类型精讲+接口示例+源码节选
Prometheus:
prometheus是什么,网上已经有很多文章了,prometheus的部署和启动可以参照这个链接。prometheus为使用者提供了http接口,使用者可以使用PromQl通过get或post来从prometheus进行query。prometheus http api传送
示例:
命名规则
在https://github.com/prometheus/common/blob/master/model/metric.go定义了prometheus metric的命名规则,即只能为大小写字母,数字,’_’,’:’,并且名字不能为空string,其他的字符均不可出现
func IsValidMetricName(n LabelValue) bool {if len(n) == 0 {return false}for i, b := range n {if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) {return false}}return true}
Client golang四种类型:
counter
计数器,并且只能增长和重置。例如:一个网站的总访问量,机器的运行时长
gauge
测量值,或瞬时记录值,可以增加,也可以减少。例如:一个视频的同时观看人数,当前运行的进程数
histogram
- 直方图,一个histogram会生成三个指标,分别是_count,_sum,_bucket。暂时为了好理解,先把_sum指标理解为Gauge,然后_count指标就是对Gauge值修改的总次数,而_bucket指标分成了几个桶子,把每次修改的值放进桶子里,不过会把值分几个层次,进入不同的桶,每个桶子里面的个数,就是直方图的纵坐标(实际情况是这个桶之前所有桶里面的个数是这个桶的纵坐标),而桶子的划分条件,就是横坐标。
- 举个铁球做例子,我们一共有1000个大小不同的铁球,质量从1kg-100kg不等,假设我分5个桶,每个桶存储不同质量的铁球,第一个桶0-20kg,第二个20-40kg,后面依此。然后1000个铁球,就是histogram的count,而1000个铁球的总质量就是histogram的sum,那么bucket就是那五个桶,当然几个桶,桶的规则怎样都是可以设计的,这五个桶每个的0-20,20-40…就是横坐标,而每个桶中的个数,就是纵坐标。根据这些数据就可以形成一个直方图。
- 因为直方图能够分出数据的质量,所以应用直方图的例子如:页面的响应时间,一块业务代码的时延
- 一个不错的例子:传送门
- 刚接触的可能会懵懵的,不过不太复杂,建议多去实践一下。
summary
- 概要,summary和histogram类似也会产生三个指标,分别是_count,_sum,和{quantile} ,count和sum与histogram的概念相同,quantile的含义是分位数,我们都知道中位数,那么中位数其实就是一个五分quantile,而summary可以在定义时指定很多分位数,如五分数,九分数,九九分数。九分数的概念就是比这个数小的数占百分之九十。
- summary和histogram类似也可以用来描述页面响应时间这类型的值,但差别是,histogram可以很容易看出来长尾值,比如投票很多时候都是去掉一个最高分,去掉一个最低分这种,长尾值就是很影响平均数的值,所有直方图能够很容易分析出问题数据。 而summary更能发现百分之多少的页面响应时间是多少,更能分析出页面响应时间的总体情况。
比较
- summary和histogram都很容易看出平均数(即_sum/_count),但是histogram容易发现影响平均数的极端值,而histogram可以直接得到分位数,两种结构可以由开发者根据数据需求自行抉择。
- histogram由clinet go直接算出分位数,而summary虽然可以算出分位数,但是更多是由开发者计算,或者调用prometheus的api获取histogram的分位数,prometheus处理函数传送门。
四种类型的接口(go):
下面图片可传送:
Counter
counter.Inc(), counter.Add(123)
Gauge
gauge.Set(), gauge.Inc(), gauge.Dec(), gauge.Add(123) , gauge.Sub(321)
Histogram
histogram.Observer(123)//添加此数据到此histogram实例(使其观察)
Summary
summary.Observer(123)
共有接口
type Metric interface {//获取此metric的描述Desc() *Desc//转化为proto格式的Metric,返回Write(*dto.Metric) error //dto "github.com/prometheus/client_model/go"}
四种类型使用:
client golang的代码均是安全的,使用了sync库下的读写锁和CAS原子操作
package main//不是伪代码,可以直接go runimport ("net/http""time""log""math""github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promhttp")var (MyTestCounter = prometheus.NewCounter(prometheus.CounterOpts{//因为Name不可以重复,所以建议规则为:"部门名_业务名_模块名_标量名_类型"Name: "my_test_counter", //唯一id,不可重复Register(),可以Unregister()Help: "my test counter", //对此Counter的描述})MyTestGauge = prometheus.NewGauge(prometheus.GaugeOpts{Name: "my_test_gauge",Help: "my test gauge",})MyTestHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{Name: "my_test_histogram",Help: "my test histogram",Buckets: prometheus.LinearBuckets(20, 5, 5), //第一个桶20起,每个桶间隔5,共5个桶。 所以20, 25, 30, 35, 40}) MyTestSummary = prometheus.NewSummary(prometheus.SummaryOpts{Name: "my_test_summary",Help: "my test summary",Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, //返回五分数, 九分数, 九九分数}))func main() {//不能注册多次Name相同的Metrics//MustRegister注册失败将直接panic(),如果想捕获error,建议使用Register()prometheus.MustRegister(MyTestCounter)prometheus.MustRegister(MyTestGauge)prometheus.MustRegister(MyTestHistogram)prometheus.MustRegister(MyTestSummary)go func(){var i float64for {i++MyTestCounter.Add(10000) //每次加常量MyTestGauge.Add(i) //每次加增量MyTestHistogram.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) //每次观察一个18 - 42的量MyTestSummary.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)time.Sleep(time.Second)}}()http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil))//多个进程不可监听同一个端口}
我们看一下瞬时的值(运行之后在控制台输入curl 127.0.0.1:8080/metrics即可获取metrics):
- 我们重点看下histogram_bucket,le=“20”,代表0-20的值有12个,le=“25”,代表0-25的值有20个,le="+Inf",代表一共有55个值,和histogram的值是相同的,如果想计算30-35的值的个数,需要用le=“35” - le="30"来获取。
- summary{quantile=“0.5”},代表瞬时,中位数为32.3,summary{quantile=“0.9”},九分数为41.5。
- histogram的平均数是 sum/count = 1677.5/55 = 30.5
- summary的平均数是 sum/count = 1677.5/55 = 30.5,因为他们两个每次赋的值相同,所以平均数也肯定相同。他们的区别通过上面的数据,估计读者也应该有个了解了。
放上几张prometheus的图表,很好理解:
图像存在锯齿是因为prometheus默认每15s同步一次数据
Vector类型及其提供的接口:
不论是Counter, Gauge, Histogram, Summary,都有自己的数组形式, 我们可以直接使用。
以下是client go提供的对数组全部操作的接口。
//metricVec实现了具体结构和接口,提供给四种数组类型调用,部分接口不提供给用户//位于github.com/prometheus/client_golang/prometheus/vec.gotype metricVec struct {*metricMap curry []curriedLabelValuehashAdd func(h uint64, s string) uint64hashAddByte func(h uint64, b byte) uint64}//删除匹配的labels,删除成功返回true,如果未找到则返回false,并不是error //两者的不同: // Delete用法: vec.Delete(Labels{"l1": "v1", "l2": "v2"}) // DeleteLabelValues用法: vec.DeleteLabelValues("v1", "v2") //如果后者参数的顺序有问题,则返回false,而前者不会 //但是与之带来的是前者的开销要比后者大,因为前者要构建Labels映射func (m *metricVec) DeleteLabelValues(lvs ...string) bool{} func (m *metricVec) Delete(labels Labels) bool {} type Observer interface {Observe(float64)}//XXX需要使用Counter,Gauge,Histogram,Summary来代替//以下接口实现于counter.go, gauge.go, histogram.go, summary.go type XXXVec struct { *metricVec } //将返回数组实例,如 NewCounterVec,将返回一个 *CounterVec,//注意,调用时,opts 中, Histogtam的Budket不能有"le", Summary的quantile不能有"quantile",否则painc()func NewXXXVec(opts XXXOpts, labelNames []string) *XXXVec{} //如果CounterVec则 TTT为Counter,GaugeVec则TTT为Gauge,Histogram和Summary则TTT为Observer //获取Counter,Gauge,Histogram或Summary,如果存在则返回,不存在则创建,如果name相同,描述不同,则返回error。//用法:// vec.GetMetricWith(Labels{"l1": "v1", "l2": "v2"})// vec.GetMetricWithLabelValues("v1", "v2") //很容易因为顺序问题而导致错误或获取不到,所以建议使用前者,但与之带来的是前者会有额外消耗 //如果我们只想获取,如果获取不到不创建新的的话,那么是做不到的,不过我们不保存返回的实例就好了。如果考虑到消耗,也可以使用Delete来移除它 func (v *XXXVec) GetMetricWith(labels Labels) (TTT, error){} func (v *XXXVec) GetMetricWithLabelValues(lvs ...string)(TTT, error){} //分别为GetMetricWith和GetMetricWithLabelValues的must形式 //即如果出错则panic(),不会返回error //不建议使用must形式,因为觉得我们自己处理error的能力还是要有的,即使我们捕捉到error之后和它做相同的事func (v *XXXVec) WithLabelValues(lvs ...string) TTT{} func (v *XXXVec) With(labels Labels) TTT{} //CurryWith将根据传入的labels,进行匹配,返回xxxVec形式,xxxVec并不是数组类型!//作用为了返回子xxxVec//注意,不要对返回值进行for range,因为不是数组,并且里面的值和方法并不是公开的。//可能的使用情况:// TestHistogramVec := NewHistogramVec(HistogramVecOpts{// Name : "test_name",// Help : "test_help",// Buckets: prometheus.LinearBuckets(20, 5, 5),// },[]string{"color","size","weight"}) // TestHistogramVecSon := CurryWith("color":"black")func (v *XXXVec) CurryWith(labels Labels) (TTTVec, error){} //CurryWith的Must形式,出现异常直接panic()func (v *XXXVec) MustCurryWith(labels Labels) TTTVec{}
数组的使用:
package mainimport ("net/http""time""log""math""github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promhttp")var (MyTestHistogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "human_weight_histogram",Help: "human weight histogram",Buckets: prometheus.LinearBuckets(1, 10, 15), //第一个桶1起,每个桶间隔10, 共15个桶。 所以1,11,21,31,...,141},[]string{"sex","age","race"},))func main() {prometheus.MustRegister(MyTestHistogramVec)go func(){var i float64for i < 20 { //不要太在意赋的什么值了,随便写的,主要为了了解用法MyTestHistogramVec.With(prometheus.Labels{"sex":"man","age":"20","race":"black"}).Observe(90 + math.Floor(400*math.Sin(float64(i*127)*0.1))/10) MyTestHistogramVec.With(prometheus.Labels{"sex":"woman","age":"20","race":"black"}).Observe(70 + math.Floor(400*math.Sin(float64(i*127)*0.1))/10) MyTestHistogramVec.With(prometheus.Labels{"sex":"man","age":"25","race":"black"}).Observe(95 + math.Floor(400*math.Sin(float64(i*127)*0.1))/10) MyTestHistogramVec.With(prometheus.Labels{"sex":"woman","age":"25","race":"black"}).Observe(95 + math.Floor(400*math.Sin(float64(i*127)*0.1))/10) MyTestHistogramVec.With(prometheus.Labels{"sex":"man","age":"20","race":"yellow"}).Observe(90 + math.Floor(400*math.Sin(float64(i*127)*0.1))/10) time.Sleep(time.Second)i++}}()http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil)}
看一下结果
可以发现,有太多项了
下面我们对结果进行过滤
这样就可以对数组,或者Histogram, 或者Summary进行过滤了
promauto:
如果你是一个懒人的话,不想去手动Registor()的话,promauto提供了这种方法。
import ("github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promauto")//这时候你就不需要去调用带Registor字样的方法了。Unregistor除外!//但是因为promauto调用的是MustRegistor(xxx),所以如果注册出现问题会直接panic()var histogram = promauto.NewHistogram(prometheus.HistogramOpts{Name: "random_numbers",Help: "A histogram of normally distributed random numbers.",Buckets: prometheus.LinearBuckets(-3, .1, 61),})
//看两个promauto的实现func NewCounterFunc(opts prometheus.CounterOpts, function func() float64) prometheus.CounterFunc {g := prometheus.NewCounterFunc(opts, function)prometheus.MustRegister(g)return g}func NewSummary(opts prometheus.SummaryOpts) prometheus.Summary {s := prometheus.NewSummary(opts)prometheus.MustRegister(s)return s}func NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {h := prometheus.NewHistogramVec(opts, labelNames)prometheus.MustRegister(h)return h}
Timer:
原文链接
//原文就这么多package prometheusimport "time"// Timer is a helper type to time functions. Use NewTimer to create new// instances.type Timer struct {begin time.Timeobserver Observer}// 通常使用这种形式来Observe一个函数的运行时间// 已测试,非常好用// func TimeMe() {// timer := NewTimer(myHistogram)// defer timer.ObserveDuration()// // Do actual work.// }func NewTimer(o Observer) *Timer {return &Timer{begin: time.Now(),observer: o,}}func (t *Timer) ObserveDuration() time.Duration {d := time.Since(t.begin)if t.observer != nil {t.observer.Observe(d.Seconds())}return d}
最后附赠几个网络上文档链接
https://godoc.org/github.com/prometheus/client_golang/prometheus
https://ryanyang.gitbook.io/prometheus/
https://s0prometheus0io.icopy.site/docs/introduction/overview/
浅谈prometheus(普罗米修斯) client golang相关推荐
- Prometheus 普罗米修斯
Prometheus 普罗米修斯 Prometheus由go语言开发,是一套开源的监控.报警.时间序列数据库的组合.适合监控docker容器.因为Kubernetes (K8S) 的流行带动了prom ...
- 一篇文章搞定Prometheus普罗米修斯监控系统的部署
Prometheus普罗米修斯监控系统 一.普罗米修斯监控概述 1.1 什么是普罗米修斯监控 二.时间序列数据 2.1 什么是序列数据 2.2 时间序列数据特点 2.3 普罗米修斯特征 三.普罗米修斯 ...
- Kubernetes15——Prometheus普罗米修斯UI集群管理
上传镜像到仓库 docker load -i prometheus-operator-v0.35.0.tar docker images | grep bitnami | awk '{system(& ...
- Prometheus普罗米修斯调研笔记
项目简介: Prometheus是最初在SoundCloud上构建的开源系统监控和警报工具: 开源自2012年,许多公司和组织都采用了Prometheus,都拥有非常活跃的开发人员和用户社区: 现在它 ...
- 【监控】Prometheus(普罗米修斯)监控概述
文章目录 一.监控系统概论 二.基础资源监控 2.1.网络监控 2.2.存储监控 2.3.服务器监控 2.4.中间件监控 2.5.应用程序监控(APM) 三.Prometheus 简介 3.1.什么是 ...
- 系统监控——prometheus(普罗米修斯)的部署
系统监控--prometheus的部署 一.prometheus简介 1.优点 2.组件 3.架构 二.环境包准备 三.安装运行 四.网页访问 五.安装图形化界面 一.prometheus简介 pro ...
- Prometheus普罗米修斯部署
prometheus部署 1.获取Prometheus安装包并解压 [root@localhost /]# wget https://github.com/prometheus/prometheus/ ...
- prometheus普罗米修斯集监控外部k8s
prometheus监控k8s集群 实现思路 pod性能: 使用cadvisor进行实现,监控容器的CPU.内存利用率 Node性能: 使用node-exporter实现,主要监控节点CPU. ...
- Prometheus普罗米修斯监控+Alertmanager预警+钉钉预警
简单实操,可直接拷贝命令执行 前提条件: centos7 10.11.7.95 关闭selinux vi /etc/sysconfig/selinuxSELINUX=disabled setenfo ...
- 普罗米修斯监控linux,Prometheus(普罗米修斯)搭建监控
Prometheus(普罗米修斯) 实验环境:(各个主机,ip,所需服务) docker01 docker02 docker03 1.10 1.20 1.30 NodeEXporter NodeEXp ...
最新文章
- python是最好的语言 永远二十岁_Python是世界上最好的语言吗?
- CocoaPods 原理分享及遇到的问题改进
- 什么原数据更容易平稳_【时间序列】-航空数据预测
- 访谈Stuart Davidson:Skyscanner的持续交付推广
- 个人博客系统分享(可打包直接上云)
- 《C++ Primer》13.1.3节练习
- scala与java的区别_Scala学习笔记及与Java不同之处总结
- Kafka(一)背景和概念
- 那些年,备胎一起追的女神
- 数据科学研究的现状与趋势全解
- 织梦模板(dedecms) 文章页调用 浏览次数(阅读量,访问量)
- GPS卫星同步时钟系统(北斗授时服务器)的技术研究
- 景区介绍界面(Android)
- 方寸微 T620 存储安全芯片 安全存储 USB3.0 U盘/移动硬盘
- 融合差分变异策略和自适应调整权重的改进蝴蝶优化算法
- html网页右侧悬浮代码,html悬浮窗口代码
- 转转转转转转转转转转转转转转转转转转转转转转转转转
- 【soft6星评论】站在5G时代的入口,我们看到了“智慧交通”的含苞待放
- 利用Word 中的“从任意文件中恢复文本”转换器来恢复损坏的文档
- android app源码大全_[源码和文档分享]基于Android的家庭学校联系平台APP开发与实现...