Prometheus Exporter开发

Prometheus Exporter开发

Exporter数据格式介绍
Exporter徒手开发
Exporter4种指标类型演示
Exporter自定义注册表
Exporter 基于Collector的模块开发

Exporter介绍

 数据格式

通讯协议
        HTTP协议服务端实现了gzip
数据格式

        text/plain:文本协议

数据格式
prometheus是拉取数据的监控模型它对客户端暴露的数据格式要求如下

 Exporter徒手开发

package main

import (
	"fmt"
	"net/http"
)

func HelloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "lexporter_request_count{user=\"admin\"} 1000")
}

func main() {
	http.HandleFunc("/metrics", HelloHandler)
	http.ListenAndServe(":8050", nil)
}

 SDK 软件开发套件(Software development kit)

Prometheus 提供了专门的Handler

package main

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() { 
	// Serve the default Prometheus metrics registry over HTTP on /metrics
	http.Handle("/metrics", promhttp.Handler())
	http.ListenAndServe(":8050", nil)
}

 指标类型

 Exporter 4种指标类型演示

Gauges

是最常见的Metric类型,也就是我们说的实时指标,值是什么就返回什么,并不会进行加工处理
SDK提供了该指标的构造函数: NewGauge

	queueLength := prometheus.NewGauge(prometheus.GaugeOpts{
		// Namespace,Subsystem,Name 会拼接成指标的名称: wu123_mcube_demo_queue _length
        // 其中Name是必填参数
		Namespace: "wu123",
		Subsystem: "mcube_demo",
		Name:      "queue_length",
		// 指标的描述信息
		Help: "The number of items in the queue.",
		// 指标的标签
		ConstLabels: map[string]string{
			"module": "http-server",
		},
	})

Set: 设置指定的值
Inc: +1.
Dec:-1
Add: +n
Sub:-n

# HELP wu123_mcube_demo_queue_length The number of items in the queue.

# TYPE wu123_mcube_demo_queue_length gauge

wu123_mcube_demo_queue_length{module="http-server"} 100

// 测试用例
func TestGauge(t *test.T) {
	queueLength := prometheus.NewGauge(prometheus.GaugeOpts{
		// Namespace,Subsystem,Name 会拼接成指标的名称: magedu _mcube_demo_queue _length// 其中Name是必填参数
		Namespace: "magedu",
		Subsystem: "mcube_demo",
		Name:      "queue_length",
		// 指标的描述信息
		Help: "The number of items in the queue.",
		// 指标的标签
		ConstLabels: map[string]string{
			"module": "http-server",
		},
	})

	queueLength.Set(100)
...
}

 Counter

Counters是计算器指标 用于统计次数使用通过 prometheus.NewCounter0 函数来初始化指标对象

	totalRequests := prometheus.NewCounter(prometheus.CounterOpts{
		Name: "http_requests_total",
		Help: "The total number of handled HTTP requests.",
	})

lnc: +1:计数器增加1

Add: +n:计数器增加23.

# HELP http_requests_total The total number of handled HTTP requests.

# TYPE http_requests_total counter

http_requests_total 10

func TestCounter(t *testing.T) {
	totalRequests := prometheus.NewCounter(prometheus.CounterOpts{
		Name: "http_requests_total",
		Help: "The total number of handled HTTP requests.",
	})

	for i := 0; i < 10; i++ {
		totalRequests .Inc()
	}
...
}

 Histogram概念

Histograms 被叫主直方图或者柱状图,主要用于统计指标值的一个分布情况

Bucket: 设置横轴区间只设置上限 不设下限

0~100

0~90

0~80

0~70

0~60 

在监控里面,Histograms 典型的应用场景 就是统计 请求耗时分布,比如

0~100ms 请求个数

0~500ms 请求个数

0~5000ms 请求个数

为什么不用平均值来进行统计? 不合适

Histograms用法

我们使用NewHistogram初始化一个直方图类型的指标:

	requestDurations := prometheus.NewHistogram(prometheus.HistogramOpts{
		Name: "http reguest duration seconds",
		Help: "A histogram of the HTTP request durations in seconds.",
		// Bucket 配置:第一个 bucket 包括所有在 0.05s 内完成的请求,最后一个包括所有在10s内完成的请求。
		Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10},
	})

observe: 加入样板进行统计

Histograms演示

le="+Inf",表示小于正无穷,也就是统计所有的含义
后缀_sum,参加统计的值的求和
后缀_count 参加统计的值得总数

# HELP http_request_duration seconds A histogram of the HTTP request durations in seconds.
# TYPE http_request_duration seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 2
http_request_duration_seconds_bucket{le="0.1"} 2
http_request_duration_seconds_bucket{le="0.25"} 2
http_request_duration_seconds_bucket{le="0.5"} 4
http_request_duration_seconds_bucket{le="1"} 6
http_request_duration_seconds_bucket{le="2.5"} 6
http_request_duration_seconds_bucket{le="5"} 6
http_request_duration_seconds_bucket{le="10"} 7
http_request_duration_seconds_bucket{le="+Inf"} 8
http_request_duration_seconds_sum 18.53
http_request_duration_seconds_count 8

func TestHistogram(t *testing.T) {
	requestDurations := prometheus.NewHistogram(prometheus.HistogramOpts{
		Name: "http_request_duration _seconds",
		Help: "A histogram of the HTTP request durations in seconds.",
		// Bucket 配置:第一个 bucket 包括所有在 0.05 内完成的请求,最后一个包括所有在10s内完成的请求
		Buckets: []float64(0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10),
	})
	//添加值
	for _, v := range []float64(0.01, 0.02, 0.3, 0.4, 0.6, 0.7, 5.5, 11){
		requestDurations.Observe(v)
...
}

指标的值是怎么来的?
自己理解:

0~0.05 0~0.1 0~0.25 0~0.5 0~1 0~2.5 0~5 0~10 0~+Inf
0.01
0.02
0.3
0.4
0.6
0.7
5.5
11
总计 2 2 2 4 6 6 6 7 8

sum:这8个数据的值
count表示:总共有8个数据

Histograms与分位数

n%的在哪?比如说89分

histogram_quantile是服务端把histogram转换为quantile的,但是会有精度损失

客户端怎么算这个值呢?直接算好上传到服务端,不会有精度损失。
就是summary

 Summaries
Observe,用于统计数据

用NewSummary来构建该类指标
这种类型的指标 就是用于计算分位数(quantile)的,因此他需要配置一个核心参:你需要统计那个(百)分位的数据

	requestDurations := prometheus.NewSummary(prometheus.SummaryOpts{
		Name : "http_request_duration_seconds",
		Help: "A summary of the HTTP request durations in seconds .",
		Objectives: map[float64]float64{
			0.5: 0.05, // 第50个百分位数,最大绝对误差为0.05。
			0.9: 0.01, // 第90个百分位数,最大绝对误差为0.01。
			0.99: 0.001, // 第99个百分位数,最大绝对误差为0.001。
		},
	}

0.5 50%的请求在哪个区间以下

 

func TestSummary(t *testing.T) {
	requestDurations := prometheus.NewSummary(prometheus.SummaryOpts{
		Name: "http_request_duration_seconds",
		Help: "A summary of the HTTP request durations in seconds.",
		Objectives: map[fioat64]float64{
			0.5: 0.05, // 第50个百分位数,最大绝对误差为0.05。
			0.9: 0.01, // 第99个百分位数,最大绝对误差为0.01。
			0.99: 0.001, // 第98个百分位数,最大绝对误差为8.001。
		},
	})

	//添加值
	for	_, v := range []float64{0.01, 0.2, 0.3, 0.4, 0.6, 0.7, 5.5, 11} {
		requestDurations.Observe(v)
	}
...
}

指标标签

标签分为2类:
静态标签:constLabels,在指标创建时,就提前声明好,采集过程中永不变动

动态标签: variableLabels,用于在指标的收集过程中动态补充标签,比如kafka集群的exporter 需要动态补充instance id


要让你的指标支持动态标签 有专门的构造函数对应关系如下!
。NewGauge() 变成NewGaugeVec() 
。NewCounter()  变成NewCounterVec() 
。NewSummary()  变成NewSummaryVec() 
。NewHistogram()  变成NewHistogramVec() 

指标标签

# HELP wu123_mcube_demo_queue_length The number of items in the queue.

# TYPE wu123_mcube_demo_queue_length gauge

wu123_mcube_demo_queue_length{instance_id="rm_001",instance_name="kafka01",module="http-server"} 100

 下面以NewGaugeVec为例进行讲解
NewGaugeVec相比于NewGauge只多出了一个labelNames的参数

func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec

一定声明了labelNames,我们在为指标设置值得时候就必须带上对应个数的标签(一一对应,二维数组)

queueLeneth.WithLabelValues("rm_001", "kafka01").Set(100)

module是静态标签

指标注册

Prometheus 定义了一个注册表的接口

// 指标注册接口
type Registerer interface {
    // 注册采集器,有异常会报错
    Register(Collector) error
    // 注册采集器,有异常会panic
    MustRegister(...Collector)
    // 注销该采集器
    Unregister(Collector) bool
}

默认注册表
Prometheus 实现了一个默认的Registerer对象 也就是默认注册表

var (
    defaultRegistry  = NewRegistry()
    DefaultRegisterer Registerer = defaultRegistry
    DefaultGatherer   Gatherer   = defaultRegistry
)

我们通过prometheus提供的MustRegister可以将我们自定义指标注册进去

// 在默认的注册表中注册该指标
prometheus.MustRegister(temp)
prometheus.Register()
prometheus.Unregister()

默认注册表

package main

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
	// 创建一个gauge类型的指标
	queueLength := prometheus.NewGauge(prometheus.GaugeOpts{
		// Namespace,Subsystem,Name 会拼接成指标的名称: magedu _mcube_demo_queue _length// 其中Name是必填参数
		Namespace: "wu123",
		Subsystem: "mcube_demo",
		Name:      "queue_length",
		// 指标的描述信息
		Help: "The number of items in the queue.",
		// 指标的标签
		ConstLabels: map[string]string{
			"module": "http-server",
		},
	})

	// 在默认的注册表中注册该指标
	prometheus.MustRegister(queueLength)

	// 设置gauge的值为100
	queueLength.Set(100)

	// Serve the default Prometheus metrics registry over HTTP on /metrics
	http.Handle("/metrics", promhttp.Handler())
	http.ListenAndServe(":8050", nil)
}

默认注册表添加了很多其他指标的采集

我不想要添加默认指标,怎么做?

自定义注册表

使用NewRegistry()创建一个全新的注册表
通过注册表对象的MustRegister把指标注册到自定义的注册表中


暴露指标的时候必须通过调用 promhttp.HandleFor() 函数来创建一个专门针对我们自定义注册表的 HTTP 处理器,我们还需要在promhttp.HandlerOpts 配置对象的 Registry 字段中传递我们的注册表对象

...
// 暴露指标
http.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{Registry: registry}))
http.ListenAndServe(":8050", nil)

自定义注册表

# HELP wu123_mcube_demo_queue_length The number of items in the queue.

# TYPE wu123_mcube_demo_queue_length gauge

wu123_mcube_demo_queue_length{module="http-server"} 100

func main() {
	// 创建一个自定义的注册表
	registry := promprometheus.NewRegistry()

	// 创建一个gauge类型的指标
	queueLength := prometheus.NewGauge(prometheus.GaugeOpts{
		// Namespace,Subsystem,Name 会拼接成指标的名称: magedu _mcube_demo_queue _length// 其中Name是必填参数
		Namespace: "wu123",
		Subsystem: "mcube_demo",
		Name:      "queue_length",
		// 指标的描述信息
		Help: "The number of items in the queue.",
		// 指标的标签
		ConstLabels: map[string]string{
			"module": "http-server",
		},
	})


	// 设置gauge的值为100
	queueLength.Set(100)

	// 在自定义的注册表中注册该指标
	registry.MustRegister(queueLength)
}

我想要添加默认指标,怎么做

其实Prometheus在客户端中默认有如下Collector供我们选择

只需把我们需要的添加到我们自定义的注册表中即可

// 添加 process 和 Go 运行时指标到我们自定义的注册表中registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
registry.MustRegister(prometheus.NewGoCollector())

Collector接口解读

# HELP wu123_mcube_demo_queue_length The number of items in the queue.

# TYPE wu123_mcube_demo_queue_length gauge

wu123_mcube_demo_queue_length{instance_id="rm_001",instance_name="kafka01",module="http-server"} 100

下面是Collector接口声明:

type Collector interface {
    // 指标的一些描述信息,就是# 标识的那部分
    // 注意这里使用的是指针,因为描述信息 全局存储一份就可以了
    Describe(chan<- *Desc)
    // 指标的数据,比如 promhttp_metric_handler_errors_total{cause="gathering"} 0
    // 这里没有使用指针,因为每次采集的值都是独立的
    Collect(chan<- Metric)
}

Collector开发

func NewDemoCollector() *DemoColector {
    return &DemoCollector{
        queueLengthDesc: prometheus.NewDesc(
            "wu123 mcube demo queue length",
            "The number of items in the queue.",
            // 动态标签的key列表
            []string{"instnace_id","instnace_name"},
            // 静态标签
            prometheus.Labels{"module": "http-server"},
        ),
        // 动态标的value列表,这里必须与声明的动态标签的key一一对应
        labelValues: []string{"mq 001", "kafka01"},
    }
}

type DemoCollector struct {
    queueLengthDesc *prometheus.Desc
    labelValues    []string
}

func (c *DemoCollector) Describe(ch chan<- *prometheus.Desc) {
    ch <- c.queueLengthDesc
}

func (c *DemoCollector) Collect(ch chan<- prometheus.Metric) {
    ch <- prometheus.MustNewConstMetric(c.queuelengthDesc, prometheus.GaugeValue, 100, c.labelValues...)
}

Collector注册

package main

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
	// 创建一个自定义的注册表
	registry := promprometheus.NewRegistry()

    // 可选:添加process和Go运行时指标到我们自定义的注册表中                 
   registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
    registry.MustRegister(prometheus.NewGoCollector())

    // 在自定义的注册表中注册采集器
	registry.MustRegister(NewDemoCollector())

	// Serve the default Prometheus metrics registry over HTTP on /metrics
	http.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{Registry: registry}))
	http.ListenAndServe(":8050", nil)

mcube-demo/protocol

metric_test.go

http.go

grpc.go

问题解答
使用场景?
内存开销怎么样?
gc的情况?
埋点
为了更好的观测程序
内容介绍
被监控端部署exporter,exporter会收集对应系统或者程序的指标生成prometheus识别的格式,对应通过IP:端口/metrics网页形式暴露,prometheus就可以收集起来,最后可以通过grafana图形界面展示。这里是exporter的开发,也是收集器的开发
 

猜你喜欢

转载自blog.csdn.net/niwoxiangyu/article/details/130456317
今日推荐