统一规则引擎:条件、防抖与动作执行的设计取舍
“温度超限后告警”和“满足条件后调用另一个系统”有相似的前半段:读取数据、判断条件、决定是否触发。我用一套 when/then 模型承载它们,但相同的数据结构并不意味着所有动作都有相同的执行风险。
更新于 2026-09-09。根据 RuleEngine、RuleStateManager、ActionExecutor 等源码核对;示例阈值用于解释规则语义,不是设备运行建议。
为什么统一模型
规则可以概括为:
Rule = 目标范围 + when 条件 + then 动作列表
条件和防抖不需要因为输出是告警还是 HTTP 请求而各实现一次。统一后,配置加载、校验、启停和热重载都可以复用。
但采集触发器与业务规则需要分开。触发器决定本轮是否读取,规则在数据读出后判断业务条件。把两者混在一起,容易出现“没有采集到数据,却声称完成阈值判断”的含义错误。
条件树与规则级防抖
when 支持单条件与 all、any 组合。概念示例是“温度高于阈值,并且设备处于运行状态”。比较时要检查点位是否存在、类型是什么,而不是默认所有输入都能转换成浮点数。
当前防抖按规则整体计时:holdSeconds 表达组合条件持续满足一段时间后触发。子条件上的独立计时不在当前语义内,不能在配置界面暗示已经支持。
状态流转为:
IDLE ──条件满足──→ TIMING ──达到持续时间──→ FIRING
↑ │ │
└────条件不满足───────┴──────条件不满足─────────┘
持续时间为零时可以直接触发;进入 FIRING 后不每轮重复执行动作。条件恢复后,下一次持续满足才能形成新的触发过程。
这里的“持续”来自采样观察,不是对每个物理时刻的证明。设备在两个采样点之间发生短暂变化,采集程序可能看不到;采集间断是否重置计时,也需要独立策略。
局部原子更新与整条执行链不是同一个保证
RuleStateManager 使用 ConcurrentHashMap.compute 更新单个 ruleId 的状态,避免普通读、改、写之间的竞争。这是有用的局部约束。
但当前实现还会在更新之后读取可变的触发与恢复标记。局部 compute 并不能直接证明跨线程的标记消费、外部动作和恢复清理都恰好执行一次。因此文章不再把这个结构概括为整个引擎“并发安全”。
进一步完善时,可以让一次状态转换返回不可变的结果,明确本轮是否触发、是否恢复,并将同设备重叠采集纳入测试。全局规则如果应用于多台设备,还需要核对状态键是否表达了正确的设备范围。
动作支持范围应以执行器为准
| 动作 | 当前源码行为 | 不能据此推导的结论 |
|---|---|---|
alert |
生成阈值告警,关联 ruleId | 完成设备故障诊断 |
write |
经 Session 写入目标地址 | 工艺过程已经达到目标状态 |
http |
顺序调用 HTTP,设置请求超时并记录结果 | 已具备异步任务恢复和业务补偿 |
delay |
记录不支持的警告并跳过 | 已实现延迟调度 |
这次修订中特别核对了 delay:它曾出现在设计的动作集合里,但 ActionExecutor 仍是跳过分支。因此不能把规划中的调度方案写成已实现功能。
HTTP 动作在当前路径中会等待请求返回,默认超时为 5 秒;多个动作还可能累计等待。异常捕获可以阻止普通异常直接冒出,但不能阻止慢请求占用采集线程。
如果将动作放到独立队列,需要继续定义队列容量、取消、顺序、幂等和过载处理。这是运行模型的变化,不是简单添加 @Async。
告警键为什么需要 ruleId
同一设备可能同时触发高温和低压规则。如果只按“设备 + 告警类型”保存,两条阈值告警可能覆盖。
加入 ruleId 后,可以按 deviceId:type:ruleId 定位规则告警,恢复时也只清理对应规则。这个变化会影响触发、查询、恢复和界面索引,不能只修改一个 Map 的键。
规则配置变更后,也需要讨论旧告警如何处理:删除规则不意味着历史事件没有发生;修改阈值后沿用旧的防抖状态是否合适,应有明确策略。
热重载:构造快照后替换
读取频繁而配置修改较少时,可以把解析、校验和按设备预合并放在重载阶段。读取时直接使用不可变集合,减少每轮重复合并。
概念上的实现方式是:
// 示意:完整构造后发布,避免读者看到 clear/putAll 的中间状态。
Map<String, List<RuleConfig>> next = buildValidatedSnapshot(configs);
snapshot = next; // snapshot 为 volatile 引用
这保证的是对应引用的可见性。若一次配置版本由多个独立 volatile 字段组成,还要考虑读者是否混用不同版本。预合并减少了列表分配,也不能等同于整个采集路径零分配。
验证从规则语义出发
有意义的用例包括:阈值两侧反复变化、持续时间边界、读取间断、同设备并发、多个规则同时告警、重载时条件变更,以及 HTTP 超时后后续动作如何处理。
对写动作,保存规则、加载规则、启用规则和实际触发必须分别核验。这一原则也适用于通过 AI 创建规则,参见AI 平台架构。
统一模型让功能可以复用,明确的执行语义才让复用可信。当前规则引擎适合解释条件求值、防抖、告警关联与配置更新;任务恢复和可靠副作用执行仍需专门建设。