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 的架构其实非常简单粗暴,简单到只有三个核心角色,我把它们称为“监控铁三角”:

  1. Prometheus Server(大脑): 它是个不知疲倦的爬虫。它不被动接收数据,而是主动出击(Pull 模式),定期去各个目标地址“拉取”数据。
  2. Pushgateway(缓存站): 专门处理那些“短命”的任务。比如一个只运行 5 秒的批处理脚本,Server 还没来得及拉取它就挂了,这就需要它先把数据推给 Pushgateway。
  3. 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
posted on 2026-01-14 19:43  ljbguanli  阅读(0)  评论(0)    收藏  举报