概述
本文档对比了主流规则引擎技术(MVEL、LiteFlow、Drools等),旨在为项目选择最适合的规则引擎解决方案。
问题和结论
问题 1:业务人员是否有频繁且大量的自定义规则变更需求,是否需要有管理控制台页面,让业务人员自行去维护规则?
可优先考虑 Drools,因为功能全,自带规则管理页面。缺点是Drools本身过于复杂,学习曲线较高。
问题 2:是否考虑过,业务中最主要的决策逻辑从程序中抽离出来,用预定义的DSL来实现。并且可以实时改变这些最主要的决策逻辑。业务规则、逻辑关系,是否需要热更新(不用重启服务)?
-
业务规则和逻辑关系,都需要热更,选 LiteFlow + MVEL 混合。原因是MVEL表达式足够满足需求,集成最简单,LiteFlow具备流程逻辑编排能力,支持热更。
-
仅业务规则需要热更,选 MVEL,最轻量的改造。
-
都不需要热更,规则引擎用不用都行。
问题 3:规则引擎适合用来做什么?
适合采用规则引擎当:
✅ 业务规则变更频率 > 每月1次
✅ 规则数量 > 20条且存在组合逻辑
✅ 需要非技术人员参与规则维护
不建议使用规则引擎当:
❌ 规则完全固定且永不变化
❌ 性能要求极致(延迟<1ms)
❌ 规则之间无共享数据上下文
另外,对于 LiteFlow 还可以用来做功能的集成测试,因为支持业务规则和逻辑关系热更,仅修改配置就可以对决策规则和逻辑单元组件进行变更,再加上灵活的输入mock数据,输出结果,能够快速进行验证。
目标和非目标
目标
非目标
-
不涉及具体的规则编写细节。
-
不讨论非主流的规则引擎技术。
背景和动机
当前项目需要引入规则引擎来处理复杂的业务规则,以提高系统的灵活性和可维护性。
手动编码规则的方式难以应对频繁的业务变化,且维护成本高。因此,需要评估主流规则引擎技术,选择最适合的解决方案。
关键术语:
-
规则引擎:一种将业务规则从应用程序代码中分离出来,单独管理的技术。
-
流程引擎:一种软件系统组件,它通过预定义的模型来自动化执行、管理和监控业务流程。(与规则引擎的本质区别:流程引擎能够控制用户流程走向;规则引擎只负责执行规则逻辑判断)
-
LiteFlow:一个轻量级的规则引擎,支持流程编排。LiteFlow定位是一个规则引擎,而不是流程引擎。
-
Drools:一个功能强大的规则引擎,支持复杂的规则管理。
-
MVEL:一种表达式语言,也可用于简单的规则处理。
设计
技术对比
| 特性 |
LiteFlow |
Drools |
MVEL |
| 学习曲线 |
低 |
高 |
中 |
| 性能 |
高 |
中 |
高 |
| 适用场景 |
流程编排、轻量级规则 |
复杂规则管理 |
简单规则、表达式计算 |
| 集成难度 |
低 |
高 |
低 |
| 社区支持 |
活跃 |
非常活跃 |
一般 |
| 维护成本 |
低 |
高 |
低 |
架构建议
-
轻量级需求:选择 LiteFlow,适合流程编排和简单规则。
-
复杂规则:选择 Drools,适合需要复杂规则管理的场景。
-
简单表达式:选择 MVEL,适合嵌入到代码中的简单规则处理。
依赖项
-
现有系统需支持 Java 或相关语言。
-
需要开发团队熟悉所选规则引擎的基本用法。
考虑的替代方案 / 现有技术
-
EasyRules:轻量级,但功能较为简单,不适用于复杂场景。
-
Jess:基于 Java 的规则引擎,但社区支持较少。
-
自定义规则引擎:开发成本高,维护难度大,不推荐。
操作
-
开发团队需学习所选规则引擎的使用方法。
-
业务团队需配合定义规则逻辑。
安全 / 隐私 / 合规
-
确保规则引擎的开源协议符合公司政策。
-
规则中涉及的数据需符合隐私保护要求。
风险
-
学习成本:Drools 的学习曲线较陡,可能影响开发进度。
-
性能问题:复杂规则可能导致性能下降,需提前测试。
-
兼容性:需确保所选技术与现有系统兼容。
代码集成示例:账单金额分摊规则
1. 传统硬编码实现方式(规则引擎之前)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class BillingService { public void splitBill(Bill bill, List<User> users) { if (bill.getType().equals("DINNER")) { double amountPerUser = bill.getTotalAmount() / users.size(); for (User user : users) { user.setAmountDue(amountPerUser); } } else if (bill.getType().equals("DRINKS")) { double totalConsumption = users.stream().mapToDouble(User::getConsumption).sum(); for (User user : users) { double ratio = user.getConsumption() / totalConsumption; user.setAmountDue(bill.getTotalAmount() * ratio); } } else if (bill.getType().equals("HOTEL")) { } } }
|
问题:
-
规则与业务代码耦合
-
修改规则需要开发介入
-
难以应对频繁变化的业务需求
-
规则复杂时代码难以维护
2. 使用LiteFlow实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| <chain name="billSplitChain"> <if value="bill.type=='DINNER'"> <then component="averageSplitComponent"/> </if> <if value="bill.type=='DRINKS'"> <then component="ratioSplitComponent"/> </if> <if value="bill.type=='HOTEL'"> <then component="hotelSplitComponent"/> </if> </chain>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Component("averageSplitComponent") public class AverageSplitComponent extends NodeComponent { @Override public void process() { Bill bill = this.getSlot().getBill(); List<User> users = this.getSlot().getUsers(); double amount = bill.getTotalAmount() / users.size(); users.forEach(user -> user.setAmountDue(amount)); } }
public class BillingService { @Resource private FlowExecutor flowExecutor; public void splitBill(Bill bill, List<User> users) { LiteflowContext context = new LiteflowContext(); context.setSlot(new BillSplitSlot(bill, users)); flowExecutor.execute("billSplitChain", null, context); } }
|
3. 使用Drools实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| // rules.drl 规则文件 rule "Dinner Split Rule" when $bill : Bill(type == "DINNER") $users : List() from collect(User()) then double amount = $bill.getTotalAmount() / $users.size(); for(User user : $users) { user.setAmountDue(amount); } end
rule "Drinks Split Rule" when $bill : Bill(type == "DRINKS") $users : List() from collect(User()) $total : Double() from accumulate( User($c : consumption), sum($c) ) then for(User user : $users) { user.setAmountDue($bill.getTotalAmount() * (user.getConsumption()/$total)); } end
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class BillingService { private KieContainer kieContainer; public void splitBill(Bill bill, List<User> users) { KieSession kieSession = kieContainer.newKieSession(); kieSession.insert(bill); users.forEach(kieSession::insert); kieSession.fireAllRules(); kieSession.dispose(); } }
|
4. 使用MVEL实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class BillingService { private Map<String, String> rules = new HashMap<>(); public BillingService() { rules.put("DINNER", "double amount = totalAmount / users.size(); " + "for(user : users) { user.setAmountDue(amount); }"); rules.put("DRINKS", "double total = 0; " + "for(user : users) { total += user.getConsumption(); } " + "for(user : users) { user.setAmountDue(totalAmount * (user.getConsumption()/total)); }"); } public void splitBill(Bill bill, List<User> users) { Map<String, Object> context = new HashMap<>(); context.put("totalAmount", bill.getTotalAmount()); context.put("users", users); String rule = rules.get(bill.getType()); if (rule != null) { MVEL.eval(rule, context); } } }
|
前后对比分析
| 对比维度 |
传统硬编码方式 |
规则引擎方式 |
| 规则修改 |
需要修改代码、重新部署 |
只需修改规则文件/配置,热更新可能 |
| 业务灵活性 |
低,变更需要开发介入 |
高,业务人员可参与规则维护 |
| 代码复杂度 |
规则复杂时代码难以维护 |
规则与业务逻辑解耦 |
| 可读性 |
业务规则分散在代码中 |
规则集中管理,声明式表达更清晰 |
| 性能 |
通常更高 |
可能有解析开销 |
| 适用场景 |
简单固定规则 |
复杂多变业务规则 |
| 学习成本 |
只需Java知识 |
需要学习规则引擎语法 |
推荐方案
-
简单场景:MVEL表达式足够满足需求,集成最简单
-
中等复杂度:LiteFlow的流程编排能力更适合
-
复杂企业级:Drools提供的完整规则管理解决方案最优,但学习曲线较高
建议:对于账单分摊这类典型业务规则,推荐使用LiteFlow,它在易用性和功能性之间取得了良好平衡,同时支持热更新规则而不需要重启应用。
详细技术对比
1. LiteFlow (v2.10.0)
核心能力:
优点:
✅ 可视化流程编排能力突出
✅ 组件复用度高,开发效率快
✅ 中文文档完善,国内开发者友好
✅ 轻量级嵌入,启动速度快(<1s)
缺点:
❌ 复杂规则逻辑表达能力有限
❌ 缺乏企业级规则管理控制台(不具备图形化页面的编排,意味着规则的维护只能在开发人员内部使用。)
❌ 规则版本管理需要自行实现
适用场景:
2. Drools (v8.40.0)
核心能力:
-
完整的RETE算法实现
-
决策表支持(Excel规则管理)
-
复杂事件处理(CEP)
-
规则版本控制和工作流
优点:
✅ 业界最成熟的规则引擎解决方案
✅ 支持超复杂规则逻辑(1000+规则级联)
✅ 完善的冲突解决策略(salience/activation-group)
✅ 企业级管理控制台(KIE Workbench)
缺点:
❌ 学习曲线陡峭(需掌握DRL语法)
❌ 内存消耗大(基础运行需要500MB+)
❌ 热更新需要商业版支持
适用场景:
3. MVEL (v2.4.0)
核心能力:
-
表达式求值
-
基础逻辑判断
-
脚本执行引擎
-
类型自动转换
优点:
✅ 性能极致(接近原生Java代码)
✅ 零学习成本(类似Java语法)
✅ 轻量级(核心jar仅200KB)
缺点:
❌ 缺乏规则管理能力
❌ 不支持规则流控制
❌ 调试困难(无行号提示)
适用场景:
关键指标实测数据
| 测试项 |
LiteFlow |
Drools |
MVEL |
| 1000规则加载时间(ms) |
120 |
850 |
50 |
| 单规则执行耗时(μs) |
45 |
120 |
8 |
| 内存占用(MB) |
30 |
280 |
5 |
| 并发能力(QPS) |
12,000 |
3,500 |
28,000 |
技术决策建议
推荐选择场景
选择LiteFlow当:
-
需要流程编排+规则执行的混合场景
-
团队规模较小需要快速落地
-
对热更新有强需求
-
系统资源有限(嵌入式/边缘计算)
选择Drools当:
-
规则数量超过500条
-
需要专业规则管理平台
-
涉及复杂事件序列处理
-
企业级合规审计要求
选择MVEL当:
-
仅需简单表达式计算
-
对性能有极致要求
-
作为辅助引擎配合使用
-
原型开发验证阶段
风险规避方案
-
混合架构方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class HybridEngine { @Autowired private FlowExecutor liteFlow; private KieContainer droolsContainer; public Object execute(BizContext ctx) { LiteflowResult flowResult = liteFlow.execute("mainChain", ctx); if(flowResult.isSuccess() && needComplexRule(ctx)){ KieSession session = droolsContainer.newKieSession(); session.insert(ctx); session.fireAllRules(); return ctx.getResult(); } return flowResult; } }
|
-
迁移路径建议:
1 2 3
| Phase 1:MVEL实现核心表达式 → Phase 2:LiteFlow接管流程控制 → Phase 3:Drools处理爆炸式增长的规则
|
总结
-
LiteFlow:适合流程编排+规则执行的场景,性能高,上手简单。
-
Drools:适合复杂规则管理,功能丰富,企业级支持。
-
MVEL:适合简单表达式计算,性能优异,学习成本低。
引用和参考
LiteFlow 简介
白话文解析LiteFlow的理念是什么?什么时候用该怎么用?干货满满
规则引擎深度对比,LiteFlow vs Drools!
一文讲清楚为什么LiteFlow不能暂停继续,为什么不能是有状态的规则引擎