惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Blog — PlanetScale
Blog — PlanetScale
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
I
InfoQ
A
About on SuperTechFans
T
The Blog of Author Tim Ferriss
D
DataBreaches.Net
L
LangChain Blog
F
Fortinet All Blogs
C
Check Point Blog
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
J
Java Code Geeks
月光博客
月光博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
IT之家
IT之家
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
宝玉的分享
宝玉的分享
Jina AI
Jina AI

博客园 - 程序员李铁牛

赛事报名系统开发总管理中心规划 赛事报名系统开发:角色与权限体系设计 赛事系统报名接龙高并发技术细节处理浅析 档案数字化研发手记:PDF合并内存溢出踩坑 纸质档案数字化管理系统研发手记:法院诉讼档案单套制落地案例复盘——从纸质卷宗到电子卷宗 档案数字化管理系统开发手记:人事档案专项审核与数字化的系统支撑——"凡提必审"下的技术要点 档案数字化研发手记:我们的技术栈选择——档案系统前后端选型理由(含一次"炫技"的教训) 社群团购系统开发分享——工程化(下):资金支付测试策略——资金模块 100% 覆盖,其他 60% 就够 社群团购系统类快团团源码开发——工程化(上):一套让 5 人小团队不出事故的开发规范 社群团购类快团团系统开发——RBAC 权限设计源码分析:当一个微信号有 4 种身份时怎么办 社群团购系统类快团团模式开发——微信小程序登录:code2Session 之后还有 5 件事要做 数字档案管理系统化研发手记:医院病历档案数字化——合规、病案首页 OCR 与调阅提速 数字档案系统研发手记:批量扫描任务调度——TWAIN/SANE 采集驱动的封装实践 档案数字化系统研发源码手记:置信度过滤——把 97% 识别率变成真正可交付的成果 档案数字化开发实例手记:盖章遮挡文字识别恢复 景区运营预约系统开发:景区会员体系怎么搭?3级会员模型+积分玩法 景区门票预约系统全渠道平台库存数据同步技术处理方式 系统宕机了怎么办?景区票务系统应急预案模板 景区门票预约系统开发:接入AI预测客流设计思路分析 景区预约系统开发总结:门票分时预约设计原理和落地方法 设备维修保养系统预测性维护不用深度学习?设备健康度评分的务实实现方案 档案数字化管理系统研发手记:档案权限模型——全宗-门类-案卷-文件四级控制的设计与实践 档案管理信息化系统研发手记:数字水印方案对比——可见水印 vs 盲水印(档案借阅防泄露) 景区票务系统开发实例分析:上云还是本地部署?6个维度判断 档案数字化系统源码研发手记:老旧档案去污点——中值滤波与 inpainting 效果对比 一物一码防伪溯源系统开发全栈源码串联一次扫码的完整链路追踪代码走读 景区门票预约系统票务系统的6个核心模块 景区上线门票预约系统的5个常见翻车点 景区门票预约系统之人脸识别 vs 身份证核验 vs 扫码入园,哪种最适合你? 景区门票预约系统开发深度解析之定价策略功能设计
设备运维管理系统开发实战:用规则引擎实现可配置的设备告警...
程序员李铁牛 · 2026-08-31 · via 博客园 - 程序员李铁牛

引言:告警规则是"月抛型"需求

设备运维系统的告警模块有个特点:上线之后永远在改

夏天来了,温度阈值要调;客户换了工艺,某个测点的高峰时段要豁免;出了一次事故,客户要求"温度连续超 10 分钟才算告警,瞬时超不算"……第一版我们把规则写死在代码里,半年改了 17 次,每次都要发版。第 18 次需求来的时候,我们决定重做这块——本文就是那次重做的完整复盘。

先回答选型问题:为什么不用 Drools? Drools 当然强大,但对我们的场景它有三宗"罪":学习曲线陡(DRL 语法要培训)、规则文件由开发维护(客户想要的恰恰是"自己能改")、依赖重(私有化环境的客户机器跑不动太重的栈)。而设备告警规则的形态高度收敛——无非是"什么指标、超过什么阈值、持续多久、通知谁、多久静默"——这种收敛场景,自研一个轻量引擎是划算的。

设备售后运维管理系统介绍 (2)

一、规则模型:把一条告警规则拆干净

设计 DSL 之前,先跟运营/设备人员聊清楚他们脑子里的"一条告警规则"长什么样。最终抽象出五个要素:

# 告警规则 DSL 示例:排气温度过高告警
rule_id: ALARM_EXHAUST_TEMP_HIGH
tenant: t1002
target:
  device_type: COMPRESSOR      # 适用的设备型号
  metric: exhaust_temp         # 监测的测点
condition:
  operator: ">"                # GT / LT / GTE / LTE / BETWEEN
  threshold: 85
  unit: "℃"
  duration: 10m                # 持续超过 10 分钟才触发(防瞬时毛刺)
level: WARN                    # INFO / WARN / CRITICAL
silence: 2h                    # 触发后 2 小时内不重复告警(静默期)
action:
  - notify: [DEVICE_SUPERVISOR]        # 通知对象(角色)
  - create_order:                      # 自动创建维修工单
      template: FAULT_TEMP_HIGH
      priority: HIGH
escalate:                        # 升级链
  - after: 30m
    to: [EQUIPMENT_MANAGER]

这份 DSL 设计里最重要的两个字段是 durationsilence,它们直接决定了告警系统的口碑:

  • duration(持续时间):没有它,传感器每一次毛刺都是一条告警。某客户现场电流测点每天抖几十次,告警轰炸下第一周就被全员屏蔽。
  • silence(静默期):没有它,同一个故障每分钟重复触发。告警的意义在于"唤起行动",重复噪声只会训练所有人忽略它。

二、评估引擎:滑动窗口 + 增量状态

持续时间的判断需要一个滑动窗口:"条件是否连续成立 10 分钟"。全量重算太浪费,我们用增量状态机的方式:每个"设备 × 测点 × 规则"维护一个轻量的窗口状态。

public class RuleWindow {
    private long conditionStart = -1;   // 条件首次成立的时间戳

    /**
     * 每来一条数据调用一次。返回 true 表示满足"持续 duration"可触发。
     */
    public boolean feed(DataPoint point, RuleRule rule, long now) {
        boolean hit = rule.getCondition().matches(point.getValue());
        if (!hit) {
            conditionStart = -1;        // 条件中断,窗口清零
            return false;
        }
        if (conditionStart < 0) {
            conditionStart = now;       // 条件首次成立
        }
        return now - conditionStart >= rule.getDurationMs();
    }
}

整体数据流:

MQTT 数据 → 归一化 → 按(设备,测点)路由到匹配的规则集 → 逐条 feed 窗口
        → 满足持续条件 → 查静默期 → 发布告警事件 → 通知/建单/升级

三个工程要点:

  1. 内存态 + 快照恢复。窗口状态在内存里,服务重启会丢。我们对"条件已成立超过一半 duration"的窗口做定期快照到 Redis,重启后恢复,避免重启瞬间漏告警。
  2. 按测点路由规则,不做全量匹配。几千台设备 × 几十条规则如果每次数据都全扫,CPU 立刻爆炸。构建一张 (device_type, metric) → List<Rule> 的路由表,每次数据只评估命中的三五条规则。
  3. 时间乱序容忍。断网续传的历史数据批量到达时,时间戳是乱的。窗口状态机对乱序数据的处理策略:早于"窗口状态最后更新时间"的数据直接旁路走"离线补评估"批任务,不进实时窗口,防止历史数据污染实时判断。

三、热更新:改规则不发版

规则存数据库,带版本号,服务端通过两种方式感知变更:

@Scheduled(fixedDelay = 30_000)
public void reloadRules() {
    long latest = ruleMapper.getMaxVersion();
    if (latest > loadedVersion) {
        List<AlarmRule> fresh = ruleMapper.selectByVersionAfter(loadedVersion);
        Map<String, List<AlarmRule>> newRoutes = buildRouteTable(fresh);
        // 原子替换路由表;受影响的窗口状态清空重建
        routeTable = newRoutes;
        rebuildWindows(fresh);
        loadedVersion = latest;
    }
}

规则编辑界面给运营/设备主管用,表单化的条件配置,保存后 30 秒内全网生效。上线后我们统计了一下:告警相关的发版次数从月均 2~3 次降到接近零,所有调整都在界面完成,且每次变更留有操作日志,改坏了可以一键回滚到历史版本。

四、告警风暴治理:三层防线

规则引擎解决"怎么触发",风暴治理解决"别把人淹死"。我们踩过一次事故:某车间网络闪断 5 分钟,恢复后 300 台设备的断网补传数据瞬间涌入,加上恢复触发的状态变化,10 分钟内产生了 2000 多条告警,推送通道直接被打爆。之后的防线分三层:

第一层:源头收敛。同一设备同一规则处于静默期内不重复触发;设备离线告警在设备恢复后自动关闭,不残留。

第二层:空间合并。同一车间 10 台设备同时离线,大概率是车间交换机断了——按"设备组"聚合,合并成一条"3 号车间 10 台设备离线"的组告警,而不是 10 条独立告警。合并规则本身也是可配置的(按组织、按网关、按物理位置)。

第三层:通道限流与摘要。推送通道设置每用户每分钟上限,超出的部分折叠成一条摘要:"过去 10 分钟还有 23 条告警,点击查看全部"。

数据洪峰 → 源头静默 → 组合并 → 通道限流摘要

这套防线做完备之后,再也没有出现过"告警把客户惹毛"的投诉——告警系统的成败,一半在触发准不准,一半在打扰少不少

五、踩坑记录

坑一:规则交叉冲突没检测。两条规则"A 温度 > 85 告警""A 温度 BETWEEN 60~90 正常"同时存在且都启用,行为变成玄学。后来在规则保存时做冲突检测:同设备同测点的区间规则要求互斥,检测不过不让保存。

坑二:阈值边界。">85" 到底含不含 85?不同运营人员理解不同。我们在 DSL 里强制用 GT/GTE 显式操作符,界面上也用文字写明"高于 85(不含 85)",消灭口头歧义。

坑三:规则导出没有环境隔离。测试环境调好的规则被误同步到生产,阈值是测试时临时放宽的 150℃。之后规则同步只能走"导出包 + 目标环境确认"流程,且导入时强制展示所有改动差异。

写在最后

自研规则引擎听起来像"重复造轮子",但当你的规则形态收敛、需要业务人员自助维护、还要跑在客户的低配私有化环境里时,一个几百行的轻量引擎往往比引入 Drools 更快更稳。判断标准就一条:你的规则 DSL 五年之内会不会长成另一个通用编程语言——如果不会,就大胆收敛、大胆自研。

至此,从采集(03)、评分(04)到告警(05),"数据侧"的链路讲完了。下一篇回到一线作业场景:巡检模块的扫码打卡、离线缓存与防作弊设计——那是收集真实数据的第一道关卡。

系列目录(持续更新)

  1. 设备保养工单系统开发实战:从计划自动生成到验收闭环的状态机设计
  2. 从 0 到 1 开发设备运维管理系统:整体架构设计与模块划分
  3. 设备数据采集协议怎么选?MQTT、Modbus、OPC UA 在运维场景的对比与落地
  4. 预测性维护不用深度学习?设备健康度评分的务实实现方案
  5. 用规则引擎实现可配置的设备告警策略:自研轻量引擎 vs Drools 落地对比(本文)