3. 数据采集层设计:Pull vs Push模型对比,Exporter设计模式,Client Library选型
数据采集层是整个监控体系的「眼睛」。眼睛要是瞎了,后面再好的分析工具也白搭。这一章我们聊聊采集层的三个核心问题:怎么拉数据、怎么暴露数据、用什么工具去采集。
3.1 Pull vs Push:两种模型的对决
先问个问题:你的监控系统是主动去拿数据,还是等着数据送上门?
这就是 Pull 和 Push 的本质区别。我早年做传统运维时,习惯用 Push 模型——每个机器上装个 Agent,定时把指标推到中心服务器。后来转到 Prometheus 生态,才真正体会到 Pull 模型的好处。
| 对比维度 | Pull 模型(Prometheus) | Push 模型(StatsD / Graphite) |
|---|---|---|
| 数据流向 | 监控系统主动拉取目标 | 目标主动推送数据 |
| 服务发现 | 天然支持,可动态发现目标 | 需额外注册或配置 |
| 网络控制 | 监控端可控,防火墙好配 | 目标端发起,端口管理复杂 |
| 数据可靠性 | 拉取失败可重试 | 推送失败可能丢数据 |
| 适用场景 | 长期运行的服务、K8s 集群 | 短生命周期任务、批处理 |
我的建议:核心业务服务用 Pull,临时任务用 Push。别死磕一种模型,混用才是常态。
Pull 模型有个天然优势——你可以在监控端统一控制采集频率。比如线上出问题时,我习惯把采集间隔从 15s 降到 5s,快速定位问题。Push 模型就没这么灵活,你得去改每个客户端的配置。
避坑指南:我曾经在某个金融项目中全量使用 Push 模型,结果业务高峰期客户端集体推送,直接把监控网关打挂了。后来改成 Pull + 限流,再没出过类似问题。
3.2 Exporter 设计模式:让每个服务都能被「拉」
Pull 模型的核心是 Exporter。说白了,Exporter 就是一个 HTTP 端点,把内部指标翻译成监控系统能理解的格式。
我参与过一个电商平台的监控改造,当时需要监控 Redis 集群。官方 Redis Exporter 已经很好用了,但我们有自定义的缓存命中率算法,必须自己写 Exporter。
// 一个简单的自定义 Exporter 示例
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
cacheHits = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "app_cache_hits_total",
Help: "Total number of cache hits.",
},
[]string{"cache_type"},
)
)
func init() {
prometheus.MustRegister(cacheHits)
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
写 Exporter 有几个原则:
- 幂等性:每次拉取返回的数据应该是当前快照,不依赖历史状态
- 轻量:采集逻辑不能影响业务性能,我一般控制在 10ms 以内
- 可观测:Exporter 自己也要暴露健康指标,比如采集耗时、错误数
小技巧:写 Exporter 时,记得加上 up 指标。Prometheus 默认会用这个指标判断目标是否存活。我见过不少团队忘了加,结果服务挂了都不知道。
3.3 Client Library 选型:Prometheus SDK vs StatsD
选 Client Library 其实是在选生态。我这些年用过不少,最后稳定在 Prometheus SDK 和 StatsD 两个方向上。
Prometheus SDK
如果你在用 Prometheus 生态,那 Prometheus SDK 是首选。它支持四种核心指标类型:
- Counter:只增不减,适合请求数、错误数
- Gauge:可增可减,适合内存使用率、连接数
- Histogram:分桶统计,适合请求延迟
- Summary:分位数计算,适合 SLA 监控
我个人最喜欢 Histogram。有一次排查接口慢查询,就是靠 Histogram 的桶分布,一眼看出 99% 的请求在 200ms 以内,但有个别请求飙到了 5s。用 Summary 反而看不出这种分布特征。
StatsD
StatsD 是另一种思路。它用 UDP 协议,客户端只管发,不管收。适合对性能要求极高的场景。
| 对比维度 | Prometheus SDK | StatsD |
|---|---|---|
| 传输协议 | HTTP(Pull) | UDP(Push) |
| 数据格式 | 结构化文本 | 简单字符串 |
| 性能开销 | 中等 | 极低 |
| 聚合能力 | 服务端聚合 | 客户端聚合 |
| 适用语言 | Go、Java、Python 等主流语言 | 几乎所有语言 |
选型建议:新项目优先 Prometheus SDK,生态好、易维护。如果对性能有极致要求(比如每秒百万级指标),或者需要支持老旧系统,再考虑 StatsD。
嗯,这里要注意一点。StatsD 用 UDP 意味着可能丢包。我在游戏公司时,用 StatsD 采集玩家在线数,高峰期丢包率到了 5%。后来加了本地缓存和重传机制才解决。
3.4 整体架构图
下面这张图展示了 Pull 和 Push 两种模型在整体架构中的位置,以及 Exporter 和 Client Library 的角色。
从图上能看出来,Pull 模型的核心是 Exporter,每个服务都暴露一个 /metrics 端点,Prometheus 定期来拉。Push 模型则是客户端主动把数据扔给 StatsD Server,Server 做聚合后再写入后端存储。
两种模型各有优劣。我现在的做法是:核心服务用 Pull,方便管理和排障;临时任务和批处理用 Push,避免监控系统成为瓶颈。
最后说一句:不管选哪种模型,一定要给采集层加监控。我见过太多团队,监控系统本身挂了都不知道。给 Exporter 加存活检测,给 StatsD 加队列长度告警,这些基础工作别省。