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 模板的版本管理

规则模板不是一成不变的。随着业务演进,字段会增删,逻辑会调整。怎么管理这些变化?

我的做法是:模板本身也做版本管理。

每个模板版本包含:

  1. 完整的字段定义快照
  2. 版本之间的 diff 记录
  3. 兼容性标记(向前兼容/不兼容)

举个例子,假设 v1.0 的模板有 10 个字段,v2.0 新增了一个 auto_fix 字段。如果 v2.0 标记为「向前兼容」,那么 v1.0 的规则实例在 v2.0 引擎上仍然能跑,只是 auto_fix 字段为空。如果不兼容,就得强制升级。

避坑指南:我曾经在版本升级时,直接改了模板字段的默认值,结果导致一批老规则的行为发生了变化。后来我定了一条铁律——任何字段的语义变更,都必须走版本升级流程,不能偷偷改默认值。

4.5 模板与规则实例的关系

这里有个容易混淆的概念:模板和实例。

模板是「类」,实例是「对象」。模板定义了规则的结构,实例是具体的一条规则。

比如:

  • 模板:「订单金额异常检测」定义了监控订单金额、用 3-sigma 判断、告警级别 P1
  • 实例:「双十一大促订单金额异常检测」继承了模板,但把阈值调低到 2-sigma,告警渠道加了短信

这种设计的好处是:模板升级时,实例可以选择继承新模板,也可以保留自己的定制。我在项目中遇到过,业务方希望「大部分规则跟着模板走,但特殊场景允许个性化」。这个模式正好满足。

4.6 整体结构图

说了这么多,我们来看一张图,把规则模板的整体结构串起来。

规则模板整体结构图 规则模板整体结构 规则模板 基础信息 监控定义 执行配置 响应动作 元数据层(血缘 / 变更 / 运行) 规则实例(继承模板 + 定制化参数) 版本管理(v1.0 → v2.0 → v3.0,兼容性标记) 说明:模板定义规则结构,元数据提供上下文,实例承载具体配置,版本管理控制变更 四层结构共同构成一个可维护、可演进的规则体系

从这张图可以看得很清楚:模板在最上层,下面是元数据层,再下面是实例层,最下面是版本管理。每一层各司其职,又相互关联。

4.7 模板设计的几个关键决策

最后,分享几个我在实际项目中做过的关键决策,供你参考。

决策一:字段用 JSON Schema 还是自定义结构?

我建议用 JSON Schema。原因很简单:生态成熟,校验工具多,而且天然支持嵌套和引用。自定义结构虽然灵活,但每次加字段都要改解析器,太累了。

决策二:模板和实例的存储要不要分开?

要分开。模板存一份,实例存一份。实例通过 template_id 引用模板。这样模板升级时,只需要改模板表,实例表不动。如果实例需要定制,就在实例表里加 override 字段,只存差异部分。

决策三:元数据谁来维护?

我曾经犯过一个错误——让开发手动维护血缘元数据。结果呢?上线两周就全乱了。后来我改成自动采集:从 SQL 解析器里提取依赖的表和字段,从 Git 提交记录里提取变更信息,从执行日志里提取运行数据。自动化才是元数据管理的出路。