摘要
规则引擎的核心价值在于将业务决策从代码中解耦,实现规则的热部署与可视化维护。本文以JVS-Rules为例,深入解析决策表(Decision Table)与规则流(Rule Flow)的设计原理与实现细节,包含元数据模型、规则编译、执行引擎、源码片段及性能优化策略。通过一个完整的贷款审批案例,展示如何将复杂的if-else逻辑转换为可配置的规则,并对比硬编码与规则引擎的维护成本差异。
1. 为什么需要规则引擎?
传统业务规则实现方式为硬编码:
java
if (loanAmount > 500000 && creditScore < 600) {
result = "REJECT";
} else if (loanAmount > 100000 && creditScore < 650) {
result = "MANUAL_REVIEW";
} else {
result = "APPROVE";
}
这种方式的痛点:
-
规则变更需要修改代码、重新编译、测试、上线,周期长(通常数天)。
-
业务人员无法直接参与规则维护。
-
规则数量膨胀后,代码难以阅读和维护("箭头代码"现象)。
规则引擎通过将规则定义与执行分离,实现了规则的动态管理和可视化编排。
2. JVS-Rules整体架构
JVS-Rules采用前后端分离架构:
-
前端:Vue3 + Element Plus,提供决策表、规则流、决策树的可视化编辑器。
-
后端:Spring Boot + MyBatis Plus,核心模块包括规则解析器、Rete++算法引擎、规则仓库管理。
-
存储:MySQL存储规则元数据(决策表行/列、规则流节点/边),业务数据不侵入。
核心类图:
text
RuleEngine (接口)
└── RetePlusEngine (实现)
├── RuleCompiler (将决策表编译为Rete网络)
├── FactRepository (事实对象管理)
└── Agenda (规则冲突解决策略)
3. 决策表(Decision Table)深度解析
决策表是用表格形式表示规则集合,适用于条件组合较多的场景(如折扣、风控、评分卡)。
3.1 元数据模型
决策表存储在数据库中的核心表结构(简化):
sql
-- 决策表主表
CREATE TABLE `decision_table` (
`id` varchar(32) PRIMARY KEY,
`name` varchar(128) NOT NULL,
`input_fields` json COMMENT '输入字段定义:[{name, type, label}]',
`output_fields` json COMMENT '输出字段定义',
`hit_policy` varchar(20) DEFAULT 'FIRST' COMMENT '命中策略:FIRST/ALL/UNIQUE'
);
-- 决策表规则行
CREATE TABLE `decision_rule` (
`id` varchar(32) PRIMARY KEY,
`table_id` varchar(32) NOT NULL,
`priority` int DEFAULT 0,
`conditions` json COMMENT '条件表达式列表',
`actions` json COMMENT '动作赋值列表'
);
3.2 规则编译过程
以贷款审批决策表为例:
| 金额范围 | 信用分范围 | 审批结果 |
|---|---|---|
| >500,000 | <600 | 拒绝 |
| >100,000 | <650 | 人工审核 |
| * | * | 批准 |
编译步骤:
-
解析输入字段(loanAmount, creditScore)和输出字段(result)。
-
将每一行规则转换为Rete网络中的节点。
-
生成Java表达式或MVEL脚本。
源码片段(规则编译核心,来自JVS-Rules开源仓库简化):
java
public class DecisionTableCompiler {
public static RuleSet compile(DecisionTable table) {
RuleSet ruleSet = new RuleSet();
for (DecisionRule rule : table.getRules()) {
String condition = buildCondition(rule.getConditions());
String action = buildAction(rule.getActions());
Rule r = new Rule(rule.getId(), condition, action);
ruleSet.addRule(r);
}
return ruleSet;
}
private static String buildCondition(List<Condition> conds) {
// 将条件列表拼接为Java表达式,如 "loanAmount > 500000 && creditScore < 600"
if (conds.isEmpty()) return "true";
return conds.stream()
.map(c -> c.getField() + " " + c.getOp() + " " + c.getValue())
.collect(Collectors.joining(" && "));
}
}
3.3 运行时执行流程(Rete++算法)
JVS-Rules采用的Rete++算法是经典Rete的改进版本,主要优化了节点共享和内存占用。
执行流程:
-
业务系统调用规则引擎API,传入事实对象(如LoanApplication)。
-
事实对象被插入Rete网络的根节点,沿网络向下传播。
-
在Alpha节点(单条件匹配)中进行属性判断。
-
在Beta节点(多条件组合)中进行交叉匹配。
-
命中规则后,将规则实例加入Agenda。
-
根据命中策略(FIRST)取出最高优先级规则,执行动作(设置结果字段)。
-
返回执行结果。
性能数据:在4核8G环境下,包含50条规则的决策表,单次匹配平均耗时<5ms。
4. 规则流(Rule Flow)设计
规则流用于编排多个规则集或决策表的执行顺序,支持串行、并行、条件分支。
4.1 规则流模型(数据库表结构)
java
public class DecisionTableCompiler {
public static RuleSet compile(DecisionTable table) {
RuleSet ruleSet = new RuleSet();
for (DecisionRule rule : table.getRules()) {
String condition = buildCondition(rule.getConditions());
String action = buildAction(rule.getActions());
Rule r = new Rule(rule.getId(), condition, action);
ruleSet.addRule(r);
}
return ruleSet;
}
private static String buildCondition(List<Condition> conds) {
// 将条件列表拼接为Java表达式,如 "loanAmount > 500000 && creditScore < 600"
if (conds.isEmpty()) return "true";
return conds.stream()
.map(c -> c.getField() + " " + c.getOp() + " " + c.getValue())
.collect(Collectors.joining(" && "));
}
}
4.2 规则流执行引擎(源码解析)
使用有向无环图(DAG)遍历算法,支持条件分支:
java
public class RuleFlowExecutor {
public void execute(RuleFlow flow, Map<String, Object> facts) {
Node start = flow.getStartNode();
Queue<Node> queue = new LinkedList<>();
queue.add(start);
while (!queue.isEmpty()) {
Node current = queue.poll();
if (current.getNodeType() == NodeType.RULE_SET) {
// 执行关联的决策表或规则集
ruleEngine.fire(current.getRuleSetId(), facts);
}
List<Edge> outgoing = flow.getOutgoingEdges(current.getId());
for (Edge edge : outgoing) {
// 检查边的条件表达式(如果有)
if (edge.getCondition() == null || evaluate(edge.getCondition(), facts)) {
queue.add(edge.getToNode());
}
}
}
}
}
4.3 可视化设计器实现要点
JVS-Rules前端使用Vue + 拖拽库(如vue-drag-drop)实现规则流的可视化编辑。后端存储采用JSON格式保存流程定义,前端通过canvas或SVG渲染节点和连线。节点的属性配置通过动态表单实现。
5. 实践案例:银行贷款审批系统
业务规则:
-
金额>50万且信用分<600 → 拒绝。
-
金额>10万且信用分<650 → 人工审核。
-
其他 → 批准,同时计算利率:信用分>750给予0.8折优惠。
传统硬编码:约150行Java代码,包含多个if-else嵌套。变更规则需要修改代码、编译、测试、部署,平均耗时2天。
JVS-Rules实现:
-
创建决策表,包含3行规则(拒绝、人工审核、批准)。
-
创建第二个决策表,计算利率折扣。
-
创建规则流:先执行审批决策表,若批准则执行利率计算决策表。
-
规则修改时,业务人员在Web界面编辑决策表,保存后立即生效。
效果对比:
| 维度 | 硬编码 | JVS-Rules |
|---|---|---|
| 首次开发时间 | 2天 | 2小时 |
| 规则变更耗时 | 平均2天(含测试部署) | 2分钟 |
| 业务人员参与度 | 无 | 可自行维护简单规则 |
| 规则数量上限 | 受代码复杂度限制 | 支持上千条 |
6. 性能优化与最佳实践
-
规则编译缓存 :决策表编译后的Rete网络可缓存,避免重复编译。使用
ConcurrentHashMap存储编译后的RuleSet对象。 -
索引优化:常用字段(如金额区间)建立哈希索引加速匹配。在Alpha节点中为每个字段值创建唯一索引。
-
批量事实处理:对于批处理场景,使用无状态会话并行执行,注意线程安全。
-
避免过于复杂的决策表:超过50列或100行的决策表可拆分为多级规则流,提升可维护性。
7. 总结
JVS-Rules通过决策表和规则流的设计,将传统硬编码的业务规则转换为可视化、可热部署的配置。本文从元数据模型、编译原理、执行引擎、性能优化等维度进行了深度剖析,并附带了关键源码片段。对于需要频繁变更业务规则的企业系统(如金融风控、电商促销、供应链决策),规则引擎是降低维护成本的利器。开发者可通过JVS-Rules官方文档获取完整的部署和开发指南。