4. 规则模板设计:如何设计一个通用的规则模板?字段定义与元数据管理
规则库建好了,但规则本身长什么样?
这个问题,我早年踩过坑。那时候团队刚起步,每个人写的规则格式都不一样——有人用自然语言写一大段,有人只写了个 SQL 片段,还有人把阈值写死在代码注释里。结果呢?规则数量一过百,根本没法维护。你想想看,连「这个规则到底监控什么」都看不出来,更别提自动化执行了。
所以这一章,我们来聊聊规则模板的设计。说白了,就是给规则一个「标准骨架」。
4.1 规则模板的核心要素
一个通用的规则模板,至少要回答三个问题:监控谁?怎么判断?出问题了怎么办?
我个人习惯把模板拆成四个部分:
- 基础信息:规则名称、唯一 ID、所属领域、版本号
- 监控定义:数据源、指标、触发条件
- 执行配置:调度频率、超时时间、重试策略
- 响应动作:告警级别、通知渠道、自动修复脚本
嗯,这里要注意,基础信息和监控定义是必填的,后面两个可以设默认值。我在项目中遇到过,有人把执行配置写得很复杂,结果大部分规则其实只需要「每 5 分钟跑一次」这个默认值。
核心原则:模板要「够用但不冗余」。多一个字段就多一份维护成本,少一个字段就少一种可能性。
4.2 字段定义:从抽象到具体
光说概念太虚,我们直接看一个实际模板。下面是我在项目中沉淀下来的字段结构,经过三个版本的迭代,目前比较稳定。
| 分类 | 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| 基础信息 | rule_id | string | 是 | 全局唯一,格式:{领域}_{序号} |
| 基础信息 | rule_name | string | 是 | 人类可读的名称,如「订单金额异常检测」 |
| 基础信息 | domain | string | 是 | 所属领域:支付/交易/用户/商品 |
| 基础信息 | version | string | 是 | 语义化版本,如 1.2.0 |
| 监控定义 | data_source | string | 是 | 数据源标识,对应元数据中的表或流 |
| 监控定义 | metric | string | 是 | 指标表达式,如 count(order_id) |
| 监控定义 | condition | string | 是 | 触发条件,如 value > 1000 |
| 执行配置 | schedule | string | 否 | cron 表达式,默认每 5 分钟 |
| 执行配置 | timeout | int | 否 | 超时秒数,默认 30 |
| 响应动作 | severity | enum | 是 | P0/P1/P2/P3 |
| 响应动作 | notify_channels | list | 否 | 如 [email, wechat, pagerduty] |
| 响应动作 | auto_fix | string | 否 | 自动修复脚本路径或命令 |
你看,这个模板把「监控谁」和「怎么判断」拆得很清楚。我曾经见过有人把 metric 和 condition 混在一起写,结果规则逻辑和阈值绑死,换个阈值就得重写一条规则。这其实是个设计缺陷。
4.3 元数据管理:让规则「活」起来
字段定义只是骨架,元数据才是灵魂。
什么是元数据?就是「关于数据的数据」。在规则模板里,元数据描述的是规则本身的上下文——谁创建的、什么时候改的、依赖哪些上游、影响哪些下游。
我把它分成三类:
- 血缘元数据:规则依赖的数据表、字段、ETL 任务。一旦上游变更,规则可能失效。
- 变更元数据:创建人、创建时间、最后修改人、修改原因、版本变更记录。
- 运行元数据:最近一次执行时间、执行结果、平均耗时、命中次数。
一个小技巧:把运行元数据单独存到另一个表,不要和规则定义混在一起。因为运行数据增长很快,混在一起会影响规则模板的读取性能。
为什么要管这些?我举个例子。有一次线上告警突然增多,排查了半天,发现是上游数据源改了字段类型,但规则模板里的 data_source 还是旧字段名。如果有血缘元数据,上游变更时就能自动触发规则校验,提前发现问题。
4.4 模板的版本管理
规则模板不是一成不变的。随着业务演进,字段会增删,逻辑会调整。怎么管理这些变化?
我的做法是:模板本身也做版本管理。
每个模板版本包含:
- 完整的字段定义快照
- 版本之间的 diff 记录
- 兼容性标记(向前兼容/不兼容)
举个例子,假设 v1.0 的模板有 10 个字段,v2.0 新增了一个 auto_fix 字段。如果 v2.0 标记为「向前兼容」,那么 v1.0 的规则实例在 v2.0 引擎上仍然能跑,只是 auto_fix 字段为空。如果不兼容,就得强制升级。
避坑指南:我曾经在版本升级时,直接改了模板字段的默认值,结果导致一批老规则的行为发生了变化。后来我定了一条铁律——任何字段的语义变更,都必须走版本升级流程,不能偷偷改默认值。
4.5 模板与规则实例的关系
这里有个容易混淆的概念:模板和实例。
模板是「类」,实例是「对象」。模板定义了规则的结构,实例是具体的一条规则。
比如:
- 模板:「订单金额异常检测」定义了监控订单金额、用 3-sigma 判断、告警级别 P1
- 实例:「双十一大促订单金额异常检测」继承了模板,但把阈值调低到 2-sigma,告警渠道加了短信
这种设计的好处是:模板升级时,实例可以选择继承新模板,也可以保留自己的定制。我在项目中遇到过,业务方希望「大部分规则跟着模板走,但特殊场景允许个性化」。这个模式正好满足。
4.6 整体结构图
说了这么多,我们来看一张图,把规则模板的整体结构串起来。
从这张图可以看得很清楚:模板在最上层,下面是元数据层,再下面是实例层,最下面是版本管理。每一层各司其职,又相互关联。
4.7 模板设计的几个关键决策
最后,分享几个我在实际项目中做过的关键决策,供你参考。
决策一:字段用 JSON Schema 还是自定义结构?
我建议用 JSON Schema。原因很简单:生态成熟,校验工具多,而且天然支持嵌套和引用。自定义结构虽然灵活,但每次加字段都要改解析器,太累了。
决策二:模板和实例的存储要不要分开?
要分开。模板存一份,实例存一份。实例通过 template_id 引用模板。这样模板升级时,只需要改模板表,实例表不动。如果实例需要定制,就在实例表里加 override 字段,只存差异部分。
决策三:元数据谁来维护?
我曾经犯过一个错误——让开发手动维护血缘元数据。结果呢?上线两周就全乱了。后来我改成自动采集:从 SQL 解析器里提取依赖的表和字段,从 Git 提交记录里提取变更信息,从执行日志里提取运行数据。自动化才是元数据管理的出路。