一、规则引擎架构设计:整体架构分层、规则库设计、规则解析器、规则执行引擎、决策表与决策树

大家好,我是你们的老朋友。今天咱们聊聊规则引擎的架构设计。说实话,这块内容我琢磨了好几年,踩过不少坑,也积累了一些心得。你想想看,一个规则引擎如果架构没搭好,后面改起来那叫一个痛苦。我见过太多项目,规则一多就跑不动,或者改个规则要重启系统——嗯,这些都是架构设计时没想清楚。

1.1 整体架构分层

先说说整体分层。我个人习惯把规则引擎分成四层,每一层各司其职,互不干扰。这样做的好处是,哪一层出了问题,改哪一层就行,不会牵一发动全身。

核心四层架构:

  • 接入层:负责接收外部请求,做参数校验和预处理
  • 规则解析层:把文本规则变成内存中的对象结构
  • 规则执行层:真正跑规则逻辑的地方
  • 数据持久层:存规则、存日志、存结果

我在项目中遇到过一种情况:团队把规则解析和规则执行混在一起写。刚开始觉得挺方便,后来规则量到了几百条,每次改个解析逻辑都要重新部署执行引擎,搞得大家都不敢动代码。所以,分层这件事,说白了就是给自己留后路。

规则引擎四层架构图 接入层 请求接收 · 参数校验 · 预处理 规则解析层 文本解析 · AST构建 · 规则校验 规则执行层 条件匹配 · 动作执行 · 结果聚合 数据持久层 规则存储 · 日志记录 · 结果持久化 请求 响应

1.2 规则库设计

规则库怎么设计?这问题我回答过不下十次。说白了,规则库就是规则的「家」。这个家如果乱糟糟的,找条规则比找钥匙还难。

我建议用关系型数据库来存规则元数据,用文件系统或对象存储来存规则内容。为什么这么搞?因为规则元数据经常要查(比如查某个规则属于哪个场景、谁创建的、什么时候生效),而规则内容一般是一次写入多次读取。

规则库表结构示例:

-- 规则主表
CREATE TABLE rule_definition (
    rule_id        VARCHAR(32) PRIMARY KEY,
    rule_name      VARCHAR(128) NOT NULL,
    rule_content   TEXT NOT NULL,        -- 规则内容(JSON或DSL)
    rule_version   INT DEFAULT 1,
    status         TINYINT DEFAULT 0,    -- 0:禁用 1:启用
    scene_code     VARCHAR(64),          -- 所属场景
    priority       INT DEFAULT 0,        -- 优先级
    create_time    DATETIME,
    update_time    DATETIME
);

-- 规则日志表
CREATE TABLE rule_execution_log (
    log_id         BIGINT AUTO_INCREMENT PRIMARY KEY,
    rule_id        VARCHAR(32),
    request_id     VARCHAR(64),
    input_params   JSON,
    output_result  JSON,
    execute_time   INT,                  -- 执行耗时(ms)
    create_time    DATETIME
);

我曾经犯过一个错误:把规则内容和元数据全塞在一个字段里。结果后来要按场景查询规则,不得不全表扫描,那叫一个酸爽。所以,该拆的表一定要拆,别偷懒。

1.3 规则解析器

规则解析器,就是把人类写的规则文本,变成机器能理解的数据结构。你想想看,如果规则写的是 age > 18 AND score >= 60,解析器得把它拆成「条件A:age > 18」和「条件B:score >= 60」,还得知道它们之间是「与」关系。

我个人习惯用抽象语法树(AST)来表示解析后的规则。每个节点代表一个操作符或操作数,树的结构天然支持嵌套和优先级。

解析器核心流程:

  1. 词法分析:把规则文本拆成 token 序列(比如关键字、操作符、变量名)
  2. 语法分析:根据语法规则,把 token 序列组装成 AST
  3. 语义校验:检查变量是否存在、类型是否匹配、有没有循环引用
  4. 优化转换:把 AST 转换成更适合执行的中间表示

嗯,这里要注意一点:解析器一定要做缓存。我见过一个项目,每次请求都重新解析规则,结果 CPU 全耗在解析上了。其实规则一旦发布,短时间内不会变,解析一次缓存起来就好。

1.4 规则执行引擎

执行引擎是规则引擎的心脏。它的任务很简单:拿到解析好的规则结构,配上输入参数,算出结果。但简单的事往往最难做好。

我建议把执行引擎设计成「无状态」的。什么意思?就是引擎本身不保存任何中间状态,每次执行都是独立的。这样做的好处是:可以水平扩展,一台机器扛不住了,再加一台就行。

避坑指南:我曾经把规则执行结果缓存到了引擎的内存里,结果多节点部署时,A节点和B节点的缓存不一致,导致同一个请求在不同节点上得到不同结果。后来我改用 Redis 做统一缓存,才解决了这个问题。

执行引擎的另一个关键点是「短路优化」。比如一个规则是 A AND B AND C,如果 A 已经为 false,那 B 和 C 就不用算了。别小看这个优化,规则多了以后,性能差距能到几十倍。

1.5 决策表与决策树

决策表和决策树,是规则引擎里两种常用的「高级玩法」。它们解决的是同一个问题:当规则条件组合特别多的时候,怎么让规则更清晰、更高效。

决策表,说白了就是一个二维表格。行是条件组合,列是动作结果。我一般在规则条件不超过 5 个、每个条件的取值不超过 3 个时用决策表。再多了,表格就变成「天书」了。

年龄 收入(万) 信用分 决策结果
< 25 < 10 < 600 拒绝
< 25 ≥ 10 ≥ 600 人工审核
≥ 25 ≥ 10 ≥ 600 通过
≥ 25 < 10 ≥ 600 人工审核

决策树就不一样了。它是树形结构,每个节点是一个条件判断,根据判断结果走向不同的分支。决策树的优势在于:它天然支持「先判断最重要的条件」,这样可以快速过滤掉大部分不满足的情况。

我在项目中遇到过一种场景:风控规则有 20 多个条件,如果用决策表,表格得有 2^20 行,根本没法维护。后来改成决策树,先按「是否黑名单」判断,再按「年龄」判断,最后按「收入」判断,效率直接翻倍。

选型建议:

  • 条件少(≤5个)、取值少 → 决策表,直观好维护
  • 条件多、有优先级 → 决策树,性能更好
  • 条件之间有依赖关系 → 决策树,天然支持

好了,这一章的内容就到这里。规则引擎的架构设计,说白了就是「分而治之」——把大问题拆成小问题,每层只关心自己的事。下一章咱们聊聊规则语言的设计,那可是规则引擎的「灵魂」所在。


公众号:蓝海资料掘金营,微信deep3321