Prometheus Exporter制作实战:如何用Go完成生产级监控组件

做监控这么多年,我见过太多“裸奔”的系统了。
很多兄弟以为装个 Node Exporter,再把 mysqld_exporter 跑起来,Grafana 随便拖几个酷炫的仪表盘,就算是“懂监控”了。结果呢?业务接口延迟飙升,你看 CPU 正常;订单量腰斩,你看内存正常。老板在群里咆哮“为什么用户投诉了我们才知道?!”,你盯着那几个毫无波澜的基础指标,手心全是冷汗。
这就是监控的“盲区”。
真正的监控高手,从来不满足于社区提供的标准品。业务逻辑只有你最懂,核心指标必须自己通过 Exporter 暴露出来! 无论你是想监控一个老旧的 Java 遗留系统,还是一个自研的 Go 微服务,甚至是一个没有任何接口的物理设备,掌握 Prometheus Exporter 的开发能力,就是你从“运维民工”进阶到“SRE 专家”的分水岭。
今天这篇长文,我不讲虚头巴脑的理论,直接把自己压箱底的 10 年 Exporter 开发经验掏出来。从指标设计避坑,到 Go 语言实战,再到生产级的高性能优化,全部是真枪实弹的代码和血泪教训。
看完这篇,要是还不会写 Exporter,你来顺着网线打我!
第一部分:Prometheus生态快速入门 从Server到Exporter:理解监控的骨骼
很多新手一上来就扎进代码里,结果写出来的 Exporter 要么性能极差,要么根本连不上。我们得先搞清楚,Prometheus 到底是怎么“吸血”的。
1.1 Prometheus三大核心组件的协作关系
Prometheus 的架构其实非常简单粗暴,简单到只有三个核心角色,我把它们称为“监控铁三角”:
- Prometheus Server(大脑): 它是个不知疲倦的爬虫。它不被动接收数据,而是主动出击(Pull 模式),定期去各个目标地址“拉取”数据。
- Pushgateway(缓存站): 专门处理那些“短命”的任务。比如一个只运行 5 秒的批处理脚本,Server 还没来得及拉取它就挂了,这就需要它先把数据推给 Pushgateway。
- Exporter(翻译官):这才是我们今天的主角! 绝大多数现成的软件(如 MySQL、Redis、Linux 内核)并不会天生吐出 Prometheus 能看懂的格式。Exporter 的作用就是充当“中间人”,连接到目标应用,拿数据,转换成 Prometheus 的文本格式,然后暴露一个 HTTP 接口等着 Server 来拉。
你要记住:Exporter 本质上就是一个暴露 HTTP 接口的 Web 程序,仅此而已。
1.2 Exporter的两种运行模式:独立 vs 集成
在开发前,你必须做一个战略选择:
- 独立模式 (Independent / Sidecar): 也就是如果你要监控 MySQL,你不可能去改 MySQL 的源码。所以你得写一个独立的程序(mysqld_exporter),它去连 MySQL 的库,查数据,然后自己起一个 HTTP 端口。
- 适用场景: 监控第三方中间件、黑盒系统、老旧遗留系统。
- 集成模式 (Inline / Instrumentation): 如果你在写一个 Go 语言开发的微服务,千万别傻乎乎地再写个独立 Exporter。直接在你的服务代码里引入 Prometheus SDK,开一个
/metrics接口。- 适用场景: 自研应用、微服务、内部 API。
1.3 社区Exporter的现状与局限
社区里有几百个现成的 Exporter,好用吗?好用。够用吗?绝对不够!
社区的 Exporter 只能告诉你“数据库还活没活着”、“QPS 是多少”。但是,它告诉不了你:
- “今天那个大客户的订单处理失败率是不是超标了?”
- “库存同步的逻辑是不是卡在第 3 步了?”
这些业务维度的洞察,只有你自己写的 Custom Exporter 能给。
真实 Scrape Config 配置示例:
如果你写好了一个 Exporter,跑在 192.168.1.100:8080,你只需要在 Prometheus 的 prometheus.yml 里加上这几行:
scrape_configs:
- job_name: 'my_custom_business_app'
scrape_interval: 15s # 哪怕是生产环境,15s也是个黄金标准
static_configs:
- targets: ['192.168.1.100:8080']
# 加上这个标签,以后排查问题能救命
labels:
env: 'production'
region: 'shanghai'
第二部分:指标设计论 三种指标类型的深度解析与应用
代码写得再溜,指标设计错了,整个监控系统就是垃圾。我见过太多人把 Gauge 当 Counter 用,最后画出来的图全是断崖式下跌,简直是灾难。
Prometheus 的指标类型,常用的就这四个,吃透它们,你就懂了一半。
2.1 Counter(计数器):只增不减的“守财奴”
定义: Counter 是最简单的类型,它代表一种累积的指标。就像你的汽车里程表,除非你把车(程序)砸了(重启),否则它永远只会增加,不会减少。
适用场景:
- HTTP 请求总数 (
http_requests_total) - 任务完成总数 (
tasks_completed_total) - 错误发生总次数 (
errors_total)
核心逻辑:
我们在 PromQL 里查询时,从来不直接看 Counter 的绝对值(因为它一直在涨,没意义),我们只看增长率。
也就是最常用的 rate() 函数:rate(http_requests_total[5m]) -> 这里的含义是过去 5 分钟的每秒平均请求数(QPS)。
避坑指南: 永远不要用 Counter 来记录“当前在线人数”。因为人数会减少,而 Counter 只要一减少,Prometheus 就会认为发生了一次“重置”,算出来的 rate 会瞬间变成负数或巨大的异常值!
2.2 Gauge(仪表盘):情绪波动的“心电图”
定义: Gauge 是最直观的,它可增可减,就像你的车速表,或者你银行卡里的余额。
适用场景:
- 当前内存使用率 (
memory_usage_bytes) - 当前 Goroutine 数量 (
go_goroutines) - 任务队列中积压的任务数 (
job_queue_length)
核心逻辑:
Gauge 反映的是瞬时状态。它不需要 rate(),直接看值就行。
2.3 Histogram(直方图):拒绝平均数的“照妖镜”
这是最难理解,但也最有价值的指标。
老板问你:“我们的 API 慢不慢?”
你答:“平均响应时间 200ms。”
老板满意地点头。
结果背后可能有 10% 的用户请求耗时超过 5 秒,甚至超时!平均数把这些长尾问题给“抹平”了。
Histogram 就是为了解决这个问题。 它会把观测到的数据放入一个个“桶”(Bucket)里。比如:
- 小于 0.1s 的有 100 个
- 小于 0.5s 的有 500 个
- 小于 1s 的有 800 个
适用场景:
- 请求延迟(Latency)
- 响应大小(Response Size)
通过 Histogram,我们可以计算出 P99(99分位值)。histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
这句话的意思是:99% 的请求都在这个时间内完成了。这才是真实的性能指标!
2.4 Summary(摘要):客户端的“独角戏”
Summary 和 Histogram 很像,都是算分位数的。但区别在于:
- Histogram: 客户端只负责分桶,P99 是服务端(Prometheus)算出来的。消耗服务端 CPU,但支持聚合(比如算 10 台机器整体的 P99)。
- Summary: 客户端直接算好 P99 发给服务端。不消耗服务端 CPU,但不支持聚合(你不能把 10 台机器的 P99 加起来求平均)。
结论: 除非你明确知道自己在做什么,否则90% 的场景请使用 Histogram。
高危预警:高基数标签(High Cardinality)
这是新手把生产环境搞崩的第一原因!
比如你定义了一个指标http_requests_total,然后你加了一个标签叫user_id。
假如你有 1000 万个用户。Prometheus 就要存储 1000 万条不同的时间序列。内存和磁盘会瞬间爆炸!
绝对禁止在 Label 中放入:用户 ID、订单 ID、随机 Token、邮件地址等无界数据!
2.5 指标命名规范
好的命名是成功的一半。Google SRE 推荐的命名法:
{namespace}_{subsystem}_{name}_{unit}
- Metric Name:
app_db_query_duration_seconds(清晰、带单位) - Bad Name:
db_query_time(到底是毫秒还是纳秒?是哪个 App 的?)
第三部分:Go语言实现 从理论到代码的蜕变
光说不练假把式。Go 语言是 Prometheus 的亲儿子(Prometheus 本身就是 Go 写的),所以 client_golang 库非常强大。
我们将通过 4 个步骤,手撸一个生产级的监控代码。
3.1 环境搭建
首先,初始化项目结构。别把所有代码都塞在 main.go 里,那太业余了。
mkdir my-exporter
cd my-exporter
go mod init my-exporter
go get github.com/prometheus/client_go
浙公网安备 33010602011771号