Prometheus -- 浅谈Exporter
Prometheus系统 – Exporter原理
为什么我们需要Exporter?
广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target,如下所示,Prometheus通过轮询的方式定期从这些target中获取样本数据:

Prometheus 已经成为云原生应用监控行业的标准,在很多流行的监控系统中都已经实现了 Prometheus的监控接口,例如 etcd、Kubernetes、CoreDNS等,它们可以直接被Prometheus监控,但大多数监控对象都没办法直接提供监控接口,主要原因有:
- 很多系统在Prometheus诞生之前的很多年就已经发布了,例如MySQL和Redis等。
- 它们本身不支持HTTP接口,例如对于硬件性能指标,操作系统并没有原生的HTTP接口可以获取。
- 考虑到安全性,稳定性以及代码耦合等因素的影响,软件作者并不愿意将监控代码加入现有的代码中。
这些都导致无法通过一个规范解决所有监控问题。在此背景之下,Exporter 应运而生。Exporter 是一个采集监控数据并通过 Prometheus 监控规范对外提供数据的组件。除了官方实现的Exporter如Node Exporter、HAProxy Exporter、MySQLserver Exporter,还有很多第三方实现如Redis Exporter和RabbitMQ Exporter等。

Exporter分类
- 社区提供的:
- 例如:Node Exporter,MySQL Exporter,Fluentd Exporter
- 官方文档链接:https://prometheus.io/docs/instrumenting/exporters/
- 用户自定义的:
- 用户可以基于Prometheus提供的Client Library创建自己的Exporter程序。
- 这里给出Client Go的链接:https://github.com/prometheus/client_golang
Exporter获取监控数据的方式
Exporter主要通过被监控对象提供的监控相关的接口获取监控数据,主要有如下几种方式:
- HTTP/HTTPS方式。例如RabbitMQ exporter通过RabbitMQ的HTTPS接口获取监控数据。
- TCP方式。例如Redis exporter通过Redis提供的系统监控相关命令获取监控指标,MySQL server exporter通过MySQL开发的监控相关的表获取监控指标。
- 本地文件方式。例如Node exporter通过读取proc文件系统下的文件,计算得到整个操作系统的状态。
- 标准协议方式。
Exporter规范
Prometheus 在面对众多繁杂的监控对象时并没有采用逐一适配的方式,而是制定了一套独特的监控数据规范,符合这套规范的监控数据都可以被Prometheus统一采集、分析和展现。
所有的Exporter程序都需要按照Prometheus的规范,返回监控的样本数据。以Node Exporter为例,当访问/metrics地址时会返回以下内容:
# HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",mode="idle"} 362812.7890625
# HELP node_load1 1m load average.
# TYPE node_load1 gauge
node_load1 3.0703125
Exporter返回的样本数据,主要由三个部分组成:样本的一般注释信息(HELP),样本的类型注释信息(TYPE)和样本。Prometheus会对Exporter响应的内容逐行解析:
- 如果当前行以# HELP开始,Prometheus将会按照以下规则对内容进行解析,得到当前的指标名称以及相应的说明信息:
# HELP <metrics_name> <doc_string> - 如果当前行以# TYPE开始,Prometheus会按照以下规则对内容进行解析,得到当前的指标名称以及指标类型:
# TYPE <metrics_name> <metrics_type> - 除了# 开头的所有行都会被视为是监控样本数据。 每一行样本需要满足以下格式规范:
metric_name ["{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
] value [ timestamp ]
自定义Exporter
官方给出了example可以给我们参考:
https://github.com/prometheus/client_golang/blob/main/examples/random/main.go
现在我们来解析一下:
-
定义指标
rpcDurations = prometheus.NewSummaryVec(prometheus.SummaryOpts{Name: "rpc_durations_seconds",Help: "RPC latency distributions.",Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},},[]string{"service"}, ) -
注册指标:
prometheus.MustRegister(rpcDurations) -
记录监控样本数据:
go func() {for {v := rand.Float64() * *uniformDomainrpcDurations.WithLabelValues("uniform").Observe(v)time.Sleep(time.Duration(100*oscillationFactor()) * time.Millisecond)} }() -
暴露接口
http.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer,promhttp.HandlerOpts{// Opt into OpenMetrics to support exemplars.EnableOpenMetrics: true,}, )) log.Fatal(http.ListenAndServe(*addr, nil)) -
观察监控指标
# HELP rpc_durations_seconds RPC latency distributions. # TYPE rpc_durations_seconds summary rpc_durations_seconds{service="uniform",quantile="0.5"} 4.2852774516474985e-05 rpc_durations_seconds{service="uniform",quantile="0.9"} 0.00012093205759592392 rpc_durations_seconds{service="uniform",quantile="0.99"} 0.00012093205759592392 rpc_durations_seconds_sum{service="uniform"} 0.0002537090545263203 rpc_durations_seconds_count{service="uniform"} 4
Node Exporter解析
-
初始化注册采集
// NodeCollector implements the prometheus.Collector interface. type NodeCollector struct {Collectors map[string]Collectorlogger log.Logger }NodeCollector是采集器的集合,Collectors是包含了各种采集器的集合,每个采集器在启动的时候都会将自身注册到这个Collector中。
// collector/meminfo.go func init() {registerCollector("meminfo", defaultEnabled, NewMeminfoCollector) } -
注册给Prometheus
func (h *handler) innerHandler(filters ...string) (http.Handler, error) {nc, err := collector.NewNodeCollector(h.logger, filters...)r := prometheus.NewRegistry()r.Register(nc); } -
采集指标
-
遍历Collectors,执行采集动作。
// Collect implements the prometheus.Collector interface. func (n NodeCollector) Collect(ch chan<- prometheus.Metric) {wg := sync.WaitGroup{}wg.Add(len(n.Collectors))for name, c := range n.Collectors {go func(name string, c Collector) {execute(name, c, ch, n.logger)wg.Done()}(name, c)}wg.Wait() }func execute(name string, c Collector, ch chan<- prometheus.Metric, logger log.Logger) {begin := time.Now()err := c.Update(ch)ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds(), name)ch <- prometheus.MustNewConstMetric(scrapeSuccessDesc, prometheus.GaugeValue, success, name) } -
具体采集指标实现Update接口(例如:meminfo.go)
-
Update方法传入一个只写(write only)的单向管道,首先通过getMemInfo获取内存信息,然后将内存信息发送到管道中。
// Update calls (*meminfoCollector).getMemInfo to get the platform specific // memory metrics. func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) error {var metricType prometheus.ValueTypememInfo, err := c.getMemInfo()if err != nil {return fmt.Errorf("couldn't get meminfo: %w", err)}level.Debug(c.logger).Log("msg", "Set node_mem", "memInfo", memInfo)for k, v := range memInfo {if strings.HasSuffix(k, "_total") {metricType = prometheus.CounterValue} else {metricType = prometheus.GaugeValue}ch <- prometheus.MustNewConstMetric(prometheus.NewDesc(prometheus.BuildFQName(namespace, memInfoSubsystem, k),fmt.Sprintf("Memory information field %s.", k),nil, nil,),metricType, v,)}return nil }func (c *meminfoCollector) getMemInfo() (map[string]float64, error) {...return map[string]float64{"active_bytes": ps * float64(vmstat.active_count),"compressed_bytes": ps * float64(vmstat.compressor_page_count),"inactive_bytes": ps * float64(vmstat.inactive_count),"wired_bytes": ps * float64(vmstat.wire_count),"free_bytes": ps * float64(vmstat.free_count),"swapped_in_bytes_total": ps * float64(vmstat.pageins),"swapped_out_bytes_total": ps * float64(vmstat.pageouts),"total_bytes": float64(total),"swap_used_bytes": float64(swap.xsu_used),"swap_total_bytes": float64(swap.xsu_total),}, nil }
-
-
-
查看结果:
# HELP node_memory_active_bytes Memory information field active_bytes. # TYPE node_memory_active_bytes gauge node_memory_active_bytes 5.08428288e+09# HELP node_memory_swapped_in_bytes_total Memory information field swapped_in_bytes_total. # TYPE node_memory_swapped_in_bytes_total counter node_memory_swapped_in_bytes_total 3.73191360512e+11
相关文章:
Prometheus -- 浅谈Exporter
Prometheus系统 – Exporter原理 为什么我们需要Exporter? 广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target,如下所示,Prometheus通过轮询的方式定期从这些target中获取样本…...
如何确定RocketMQ中消费者的线程大小
背景 随着物联网行业的发展、智能设备数量越来越多,随着设备活跃量过大,常常存在一些高并发的请求,形成了流量尖峰,过多的请求会压垮服务器,影响其他服务运行。因此,为了保护云端服务,需要对请求…...
OpenAPI SDK组件之Spring Aop源码拓展
Spring Aop 看这个分享的应该都用过Spring Aop,这里就不再过多介绍了它是什么了。 我抽取了Spring Aop的部分源码,通过它实现请求参数可变拦截,同时apisdk离开Spring框架,仍然可以正常运行。 讲拦截也好,通知也罢&a…...
蓝桥杯C/C++VIP试题每日一练之龟兔赛跑预测
💛作者主页:静Yu 🧡简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者 💛社区地址:前端知识交流社区 🧡博主的个人博客:静Yu的个人博客…...
为你的Vue2.x老项目安装Vite发动机吧
天下苦webpack久矣,相信作为前端开发者一定经历过在项目迭代时间较长的时候经历漫长等待的这一过程,每一次保存都会浪费掉大量时间,这是webpack这种机制所带来的问题。 于是,尤大为我们带来了新一代前端构建工具:vite…...
ZCMU--5012: 铺设道路(差分思路)
Description 春春是一名道路工程师,负责铺设一条长度为 n 的道路。 铺设道路的主要工作是填平下陷的地表。 整段道路可以看作是 n 块首尾相连的区域,一开始,第 i 块区域下陷的深度为 di。 春春每天可以选择一段连续区间 [L,R]&…...
算法模板总结(自用)
算法模板总结滑动窗口双指针算法数组相关合并两个有序数组左右指针技巧快慢指针技巧字符串相关左右指针反转字符串问题快慢指针替换空格字符问题链表相关快慢双指针删除链表的倒数第N个节点链表相交环形链表链表操作几数之和两数之和四个数组的四数之和三数之和同一数组中四数之…...
【架构师】零基础到精通——架构发展
博客昵称:架构师Cool 最喜欢的座右铭:一以贯之的努力,不得懈怠的人生。 作者简介:一名Coder,软件设计师/鸿蒙高级工程师认证,在备战高级架构师/系统分析师,欢迎关注小弟! 博主小留言…...
C++(20):三路比较运算符
C20增加了三路比较运算符<>(戏称航天飞机运算符),用于对类的比较运算符进行统一的设计。有两种使用方式:默认比较对于某些类,如果按照其成员逐一比较即可决定比较运算符的值,那么可以使用默认的三路运…...
MySQL workbench 字符集、字符序的概念与联系
在数据的存储上,MySQL提供了不同的字符集支持。而在数据的对比操作上,则提供了不同的字符序支持。 MySQL提供了不同级别的设置,包括server级、database级、table级、column级,可以提供非常精准的设置。 什么是字符集、字符序&am…...
DBA之路---数据库启动与关闭过程
DBA之路—数据库启动与关闭过程 1、启动过程 oracle启动的四个状态 shutdown、就是数据库关闭状态。 nomount模式 #启动instance ,读取参数文件、分配sga空间启动后台进程,打开alter日志和其他trace文件startup nomount #该模式下只会创建实例并不加…...
Shell文件包含
和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。 一、语法格式 Shell 文件包含的语法格式如下: . filename # 注意点号(.)和文件名中间有一空格 或 source filename 在当前bash环境下读取并执行file…...
计算机网络(六): HTTP,HTTPS,DNS,网页解析全过程
文章目录一、HTTP头部包含的信息通用头部请求头部响应头部实体头部二、Keep-Alive和非Keep-Alive的区别三、HTTP的方法四、HTTP和HTTPS建立连接的过程4.1 HTTP4.2 HTTPS五、HTTP和HTTPS的区别六、HTTPS的加密方式七、cookie和sessionsessioncookie八、HTTP状态码状态码200&…...
Android仿京东金融的数值滚动尺功能
自定义数值滚动尺,这个用的还是挺多的,例如京东金融的通过滚动尺选择金额等,而这次就是高仿京东金融的数值滚动尺。首先看看下效果图,如下:首先先给你们各个变量的含义,以免在后面的讲解中不知变量的意思,代码如下://最…...
Nginx 和 Tomcat 实现负载均衡
Nginx 和 tomcat 实现负载均衡 🏆荣誉认证:51CTO博客专家博主、TOP红人、明日之星;阿里云开发者社区专家博主、技术博主、星级博主。 💻微信公众号:微笑的段嘉许 📌本文由微笑的段嘉许原创! &am…...
【万能排序之qsort、b_sort 、s_sort】
文章目录前言:star:qsort函数函数参数qsort函数的使用:star:模拟实现万冒泡排序函数参数模拟实现b_sort注意点:star:模拟实现万能选择排序函数参数模拟实现s_sort最后前言 我们所熟悉的冒泡排序,选择排序,插入排序,二分排序等都是基于给定的一…...
利用InceptionV3实现图像分类
最近在做一个机审的项目,初步希望实现图像的四分类,即:正常(neutral)、涉政(political)、涉黄(porn)、涉恐(terrorism)。有朋友给推荐了个github上…...
【Java】CAS锁
一、什么是CAS机制(compare and swap) 1.概述 CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值。经过调查发现,…...
Linux服务器配置系统安全加固方法
1. SSH空闲超时时间建议为: 600-900 解决方案: 在【/etc/ssh/sshd_config】文件中设置【ClientAliveInterval】设置为600到900之间 vim /etc/ssh/sshd_config #将 ClientAliveInterval 参数值设置为 900 2. 修改检查SSH密码修改最小间隔 解决方案: 在【/etc/login.defs】文件…...
Codeforces Round #850 (Div. 2, based on VK Cup 2022 - Final Round)(A~E)
t宝酱紫喜欢出这种分类讨论的题?!A1. Non-alternating Deck (easy version)给出n张牌,按照题目给的顺序分给两人,问最后两人手中各有几张牌。思路:模拟。AC Code:#include <bits/stdc.h>typedef long…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...
表单设计器拖拽对象时添加属性
背景:因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...
