基于事件的工业采集可观测性:从埋点到可信指标

监控不是让仪表盘上出现更多数字,而是能够回答:采集为什么变慢,消息有没有发出去,数据是否真正写入数据库。我在已有业务事件上增加指标监听,以减少业务代码与监控实现的耦合;同时也需要检查异步执行、统计口径和时间窗口。

更新于 2026-09-09。本文按当前 MetricsEventListener 等实现修订,原题中的“零侵入”不再用来承诺运行时零开销。

从事件到指标

采集、转发和存储本来就在发布事件,供页面实时更新使用。新增指标时,可以复用这些结果,不必在每个业务分支中再写一套监控逻辑。

业务操作 → 结果事件 ┬→ 页面更新
                   └→ MetricsEventListener → MeterRegistry
                                               ↓
                                     Prometheus 抓取与图表

当前实现监听采集、连接、MQTT、存储和告警事件。Counter 表达发生次数,Timer 表达操作耗时,Gauge 读取在线数量与线程池状态。架构收益在于:事件定义业务事实,监听器负责把事实转换成观测数据。

这个边界也有代价。事件字段必须足够准确:如果发布的是“已提交写入”,监听器不能把它改名为“数据库写入成功”。如果事件没有排队耗时,Timer 就无法解释端到端延迟。

异步监听并没有独立的资源预算

当前监听方法使用 @Async("plcExecutor")。它避免在正常异步调用路径里同步执行全部监听逻辑,但与采集任务共用执行池。

因此,“代码不需要逐处增加埋点”和“运行时绝不影响采集”是两回事。共享线程池仍可能产生排队与竞争,事件积压还会让仪表盘落后于真实状态。

是否改为独立指标执行池,应通过事件量、采集延迟与队列变化判断。独立池也要有容量和拒绝策略:无限排队会增加内存,有界丢弃会使统计不完整,调用线程执行又可能把耗时带回业务路径。

我会先保持监听器工作简短,避免网络调用与复杂查询;再根据运行数据决定隔离方式。仅仅换一个线程池名字并不能完成可靠性设计。

指标名称必须对应完成阶段

指标问题 合适的数据来源 容易混淆的含义
采集是否成功 采集结果事件 设备连接成功
消息是否发布成功 发布完成结果 调用了 publish 方法
数据是否落库 存储完成结果 已加入写入队列
当前是否健康 依赖与运行状态 页面仍能访问

2026-09-07 的运行检查曾记录:MQTT 未启用时,某个转发累计量仍然增长。原因是调用侧增加计数,而发布方法可能提前返回。这个累计量不能用来证明消息已经送达。

对于这种情况,前端不应靠改变颜色解释过去。更合理的方向是拆分尝试、跳过、失败与成功,并分别记录队列状态。现有大屏已绕开该累计量的成功语义,但这不等于后端计数逻辑已经修复。

成功率的分母和窗口

成功率至少需要说明观察窗口和样本量:

窗口成功率 = 窗口内成功次数 / 窗口内总尝试次数

没有尝试时应显示无样本,而不是自动显示 100%。设备没有启用采集,也不应靠“零失败”得到满分。

如果按计数器差值计算速率,还要处理应用重启和采样间隔变化。浏览器后台定时器被延迟后,不能把较长时间累计的事件除以原来的短周期,否则会出现假的吞吐尖峰。项目大屏的后续实现已经针对这类窗口缺口做了处理。

P99 也不是一个 Timer 名称就自然具备的指标。使用 histogram 查询前,需要确认桶实际暴露、统计范围一致,并说明是单设备延迟还是合并后的分布。不能把“数据库执行耗时”当成“采集到页面可见”的总耗时。

标签越多,不一定越有用

设备 ID、协议和结果状态有助于定位问题,但每个标签组合都会形成独立时间序列。把时间戳、完整异常文本或任意请求 ID 放进标签,会使指标数量难以控制。

我的划分是:有限维度进指标,具体事件细节进日志或调用记录。查询某次失败时,再用设备和时间范围关联。Gauge 的 getter 同样需要足够轻量,避免一次抓取就触发昂贵的扫描或外部请求。

健康探针不能代替访问控制

项目有 MQTT、设备连接和存储的健康检查入口。依赖未启用、启用后不可用、当前没有设备,是不同情况,需要在详情中体现。

进程存活与业务就绪也不相同:外部依赖短暂异常,不应不加区分地变成重启进程的理由。具体健康项是否进入 liveness/readiness 分组,应依据部署目标配置。

公开部署时,管理端口分离、详情隐藏和身份授权解决不同问题。换一个端口不等于有权限控制,show-details 也不代替端点认证。

我如何评估这套设计

先用固定事件数量验证计数与耗时,再比较正常、队列积压、依赖停用、应用重启和浏览器后台恢复时的行为。关注三个结果:业务事实没有被改名,缺少样本能够被识别,监控工作没有失控地争抢采集资源。

实现入口包括 MetricsEventListener、业务结果事件和监控大屏的窗口计算。本文不提供新增压测结果;它修正的是对现有机制的解释。完整采集路径见系统架构,动作耗时问题见规则引擎。