规则语言基础:语法、条件、动作与优先级
规则语言,说白了就是策略引擎的“母语”。你配置的每一条策略,最终都要翻译成规则引擎能理解的语法。我见过不少团队,业务逻辑写得天花乱坠,结果一上规则引擎就崩——十有八九是规则语法没写对。
这一节,我们就把规则语言的四个核心要素拆开揉碎:语法定义、条件表达式、动作定义、规则优先级。掌握了这些,你才算真正入了策略引擎的门。
3.1 规则语法定义:骨架要稳
每条规则,本质上就是一个“如果…那么…”的逻辑。但不同的规则引擎,语法细节差异很大。我个人习惯用 DRL(Drools Rule Language) 来举例,因为它的语法结构清晰,而且业界用得最广。
一个标准的规则长这样:
rule "订单金额超过1000元自动打折"
when
$order: Order(amount > 1000)
then
$order.setDiscount(0.9);
System.out.println("订单已打9折");
end
你看,结构其实很简单:
- rule "规则名称":给规则起个唯一的名字,方便排查问题。
- when:条件部分,也叫 LHS(Left Hand Side)。
- then:动作部分,也叫 RHS(Right Hand Side)。
- end:规则结束。
我在项目中遇到过有人把规则写成了“when…then…when…then…”的嵌套结构,结果引擎直接报语法错误。记住:一条规则只有一个 when 和一个 then。
3.2 条件表达式:筛选的艺术
条件表达式是规则引擎的“眼睛”。它决定了哪些数据会被匹配,哪些会被忽略。
常见的条件表达式类型:
| 类型 | 示例 | 说明 |
|---|---|---|
| 比较运算 | amount > 1000 |
大于、小于、等于、不等于 |
| 逻辑运算 | amount > 1000 && status == "ACTIVE" |
与、或、非 |
| 集合运算 | items contains "手机" |
包含、不包含、成员判断 |
| 正则匹配 | phoneNumber matches "^1[3-9]\\d{9}$" |
字符串模式匹配 |
| 范围判断 | age in [18, 60] |
闭区间、开区间 |
嗯,这里要注意:条件表达式不要写得太复杂。我曾经接手过一个项目,一条规则的条件部分写了 30 多行,嵌套了 5 层括号。结果每次规则评估都要卡好几秒。后来拆成 3 条规则,性能直接翻倍。
3.3 动作定义:执行的力量
动作部分,就是规则匹配后要干的事。常见的动作包括:
- 修改事实(Fact):更新业务对象的属性。
- 插入新事实:向工作内存中添加新数据。
- 删除事实:从工作内存中移除数据。
- 调用外部服务:发送消息、记录日志、触发流程。
- 中断规则链:使用
halt()或drop停止后续规则执行。
看个实际例子:
rule "VIP用户自动升级"
when
$user: User(vipLevel == 1 && totalSpend > 50000)
then
$user.setVipLevel(2);
$user.setUpgradeTime(new Date());
insert(new VipUpgradeLog($user.getId(), "自动升级"));
System.out.println("用户 " + $user.getName() + " 已升级为VIP2");
end
这个规则做了三件事:修改用户等级、记录升级时间、插入一条日志。动作可以组合,但不要在一个动作里塞太多逻辑。我建议:一个动作只做一件事,如果逻辑复杂,拆成多条规则。
3.4 规则优先级:谁先谁后
规则优先级,是策略引擎里最容易出坑的地方。你想想看,如果两条规则同时匹配了同一个数据,谁先执行?执行顺序不同,结果可能天差地别。
常见的优先级控制方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 显式优先级 | 给规则设置数字优先级,数值越小越优先 | 业务逻辑有明确的层级关系 |
| 规则顺序 | 按照规则在文件中的先后顺序执行 | 简单的线性流程 |
| 议程分组 | 将规则分组,组内按优先级,组间按分组顺序 | 复杂的多阶段业务 |
| 冲突解决策略 | 引擎内置的排序算法(如最近使用优先) | 默认行为,不推荐依赖 |
在 DRL 中,显式优先级是这样写的:
rule "黑名单拦截"
salience 100
when
$user: User(blacklisted == true)
then
$user.setBlocked(true);
System.out.println("黑名单用户已拦截");
end
rule "VIP用户免审"
salience 50
when
$user: User(vipLevel >= 3)
then
$user.setNeedReview(false);
System.out.println("VIP用户免审");
end
这里 salience 100 比 salience 50 优先级高。所以黑名单拦截会先执行。如果用户既是黑名单又是 VIP,那他会先被拦截,不会进入免审流程——这符合业务预期。
3.5 知识体系总览
为了让你更直观地理解规则语言的四个核心要素,我画了一张结构图:
这张图把四个要素的关系理清楚了。语法是骨架,条件是筛选器,动作是执行器,优先级是调度器。四者缺一不可。
3.6 实战建议
最后,给你几个我在项目中沉淀下来的建议:
- 规则文件分模块管理:按业务领域拆分成多个 DRL 文件,别把所有规则塞到一个文件里。
- 条件表达式写注释:复杂的条件一定要加注释,方便后来人维护。
- 动作部分不要写业务逻辑:动作只负责“做什么”,不负责“怎么做”。具体的计算逻辑放到 Java 服务里。
- 优先级留出扩展空间:比如用 10、20、30 这样的间隔,别用 1、2、3。这样以后插入新规则时不用改所有优先级。