浅谈prometheus(普罗米修斯) client golang

2023-05-16

浅谈prometheus client golang

含类型精讲+接口示例+源码节选

Prometheus:

prometheus是什么,网上已经有很多文章了,prometheus的部署和启动可以参照这个链接。prometheus为使用者提供了http接口,使用者可以使用PromQl通过get或post来从prometheus进行query。prometheus http api传送
示例:
example

命名规则

在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)
counter

Gauge

gauge.Set(), gauge.Inc(), gauge.Dec(), gauge.Add(123) , gauge.Sub(321)
gauge

Histogram

histogram.Observer(123)//添加此数据到此histogram实例(使其观察)
histogram

Summary

summary.Observer(123)
summary

共有接口

    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 run

    import (
        "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 float64
            for {
				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):

瞬时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的图表,很好理解:

counter
gauge
his_count
his_sum
his_bucket
summary

图像存在锯齿是因为prometheus默认每15s同步一次数据

Vector类型及其提供的接口:

不论是Counter, Gauge, Histogram, Summary,都有自己的数组形式, 我们可以直接使用。

以下是client go提供的对数组全部操作的接口。

    //metricVec实现了具体结构和接口,提供给四种数组类型调用,部分接口不提供给用户
    //位于github.com/prometheus/client_golang/prometheus/vec.go
    type metricVec struct {
        *metricMap      
        curry []curriedLabelValue
        
        hashAdd     func(h uint64, s string) uint64
        hashAddByte 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 main

    import (
        "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 float64
            for 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)
    }

看一下结果

vec_example_qian

vec_example_res_qian

可以发现,有太多项了

下面我们对结果进行过滤

vec_example_hou

vec_example_res_hou

这样就可以对数组,或者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 prometheus
    import "time"
    // Timer is a helper type to time functions. Use NewTimer to create new
    // instances.
    type Timer struct {
        begin    time.Time
        observer 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/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浅谈prometheus(普罗米修斯) client golang 的相关文章

随机推荐

  • Python中的下划线到底什么意思?

    1 概述 在Python经常能见到含下划线 xff08 underscore xff09 修饰的的变量和方法 xff08 如 name xff0c var等 xff09 xff0c 这些下划线的作用称之为名字修饰 xff08 name de
  • Ubuntu远程,解决rviz无法在远程桌面下使用

    Ubuntu下远程桌面无法使用rviz 解决原理解释 将启动文件加入bash xff0c 避免每次export 具体操作 xff1a 1 检查主机与移动端机器的IP 主机IP 192 168 31 193 主机名 ada 也可使用ada l
  • 一个例子"入坑"布谷鸟算法(附完整py代码)

    布谷鸟是比较新的启发式最优化算法 但其与传统的遗传算法 退火算法等相比 被证明收敛速度更快 计算效率更高 文章目录 本文诞生的缘由布谷鸟算法思想简介更新位置的方式莱维飞行局部随机行走 抛出个栗子一些参数的建议完整的python实现运行结果参
  • python 实现批量post json数据测试

    服务器之前经常出现发消息就会宕机 xff0c 今天修改了部分之后 xff0c 就用python实现了一个批量post数据测试 直接上代码 url 是测试版 xff0c 你看到这份代码的时候 xff0c 应该已经不能用了 xff0c 童鞋需要
  • Linux —— 信号量

    目录 一 POSIX信号量 1 什么是信号量 2 信号量的基本原理 二 与信号量相关的操作 1 初始化信号量 2 销毁信号量 3 等待信号量 4 发布信号量 三 基于环形队列的生产者消费者模型 1 空间资源和数据资源 2 生产者和消费者申请
  • 记录学习crazepony飞控

    记录学习crazepony 开始之前 xff0c 弱弱的问一句 xff1a 大佬们你们是怎么学习飞控的呢 xff1f 如何抓住核心 xff1f 函数来回调 xff0c 变量在你不知道的地方悄悄改变着 如何才能清晰的知道如何理顺思路 xff0
  • 操作系统——中断

    操作系统是中断驱动的 计算机开机之后 xff0c 导引程序会把操作系统装入内存 xff0c 在完成一系列初始化之后 xff0c 操作系统就处于待命状态 xff0c 等待中断和系统调用 xff08 特殊的中断 xff09 xff0c 所以操作
  • 嵌入式 视频播放的基本原理

    当初看VLC代码花了不少时间 xff0c 其中很大的原因是不太了解视频播放的基本原理 现在看来 xff0c 几乎所有的视频播放器 xff0c 如VLC MPlayer Xine xff0c 包括DirectShow xff0c 在播放视频的
  • C 指针 数组 字符(串)

    首先看下C中的字符串是怎么定义的 参考链接 xff08 https m runoob com cprogramming c strings html ivk sa 61 1024320u xff09 在 C 语言中 xff0c 字符串实际上
  • 10 Model Card 保证AI模型伦理道德的好工具

    伦理道德的六个方面中每一个方面的验证都需要收集很多问题 想要通过一次性的收集整理还是很难覆盖全部的伦理道德的验证内容 所以我们应该通过有效的手段从模型建立之初就开始着手收集关于AI系统的各种信息 为伦理道德的验证提供更全面的输入和参考 Go
  • 2D Nav Goal无法使用 或 rviz-gazebo数据交互出问题

    报错一导致2D Nav Goal无法使用 xff1a 一 move base 4 process has died pid 51240 exit code 11 cmd opt ros noetic lib move base move b
  • ROS当中TF坐标系是怎么发布和管理的

    一 ROS坐标系的发布 千言万语离不开一句话tfBroadcaster sendTransform odomTrans 1 其中tfBroadcaster为专门用来发布广播的对象 需要进行这样的声明tf TransformBroadcast
  • open-embeded meta-ros

    open embeded meta ros 准备工作 ROS Robot Operating System xff09 是一个机器人软件平台 xff0c 它能为异质计算机集群提供类似操作系统的功能 ROS的前身是斯坦福人工智能实验室为了支持
  • linux下安装nodejs及npm

    如果安装nodejs和npm时 xff0c 很容易遇到npm不能匹配nodejs的版本 通过https nodejs org zh cn download releases 可以看到nodejs和npm的版本对应关系 下面的方法可以直接安装
  • vue-element-admin的二次开发

    最近也是完成了公司招聘管理系统后台的前端开发 xff0c 项目已经开始测试了近期估计就会交付使用 一直是一个人在做 xff0c 配合两个后端 xff0c 说实话这种从很多不会到一个个独立debug解决问题到最后终于完成项目的感觉真的太有成就
  • 烧写APM板的bootloader

    所需工具材料 xff1a 1 一个AVRusbasp编程器以及相应的烧写软件 xff0c 推荐progisp1 72下载链接https download csdn net download sky7723 12477620 2 AVR的US
  • APM_V2.8.0的改进说明

    APM V2 8 0版基于3DR公司出的V2 5 2版优化设计而来 xff0c 硬件功能跟V2 5 2一样 xff0c 尺寸也一样 xff0c 仍旧可以使用2 52版的外壳 不同的是V2 8 0版针对内部电路设计BUG做了改进 xff0c
  • Ardupliot飞控PID等参数加密屏蔽方法(针对Mission Planner的刷新参数等功能)

    当辛辛苦苦调试OK的参数 xff0c 被别人连上飞地面站后轻松获取 xff0c 是不是有点难受 xff1f 本文针对四旋翼Copter4 0 7和 xff08 4 1 5 xff09 最新版本调试了两个加密方法 4 0 7固件 在对4 0
  • ROS省略source devel/setup.bash的方法

    为了不每次运行程序的时候都source一次devel文件夹里的setup bash xff0c 可以打开主目录 按下Crtl 43 h 显示隐藏文件 xff0c 双击打开bashrc文件 xff0c 在最后加入 source home ca
  • 浅谈prometheus(普罗米修斯) client golang

    浅谈prometheus client golang 含类型精讲 43 接口示例 43 源码节选 Prometheus xff1a prometheus是什么 xff0c 网上已经有很多文章了 xff0c prometheus的部署和启动可