规则引擎,全称:业务规则管理系统,英文名为BRMS(Business Rule Management System)。
规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。
可降低组件业务逻辑复杂性、降低应用程序的维护和可扩展性成本的组件
适用场景
业务规则经常变化,系统需依据业务的变化,实现快速、低成本的迭代更新。
规则系统特别适合以下场景:
业务规则频繁变化 - 营销策略、风控规则等
复杂决策逻辑 - 需要多条件组合判断的场景
需要业务人员参与 - 非技术人员需要配置规则
高可配置性要求 - 不同客户需要不同的规则集
审计和合规要求 - 需要记录和追踪规则变更
因此,为了快速、低成本的更新,我们需将逻辑代码和业务代码进行解耦:
业务规则与系统代码分离,实现业务规则的集中管理
业务人员可直接管理这些业务规则,同时不需要研发人员的参与。
稳定层和变化层分离,是一种优秀的设计模式;
变化层支持可视化或配置化方式,快速进行业务规则的增删改操作,甚至支持热插拔和热更新。以减少冗长的开发和测试周期;
部分规则引擎甚至带有设计器(如drools),还可解决我们 “简式建模” 的需求。
对于一些存在比较复杂的业务规则并且业务规则会频繁变动的系统比较适合使用规则引擎,如下:
1、风险控制系统----风险贷款、风险评估
2、反欺诈项目----银行贷款、征信验证
3、决策平台系统----财务计算
4、促销平台系统----满减、打折、加价购
…
风控与反欺诈系统
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 27 28 29 30 31 32 @Rule(name = "高风险交易检测", priority = 1) public class HighRiskTransactionRule { @Condition public boolean isHighRisk (@Fact("transaction") Transaction transaction) { return transaction.getAmount() > 10000 || transaction.isInternational(); } @Action public void flagForReview (@Fact("transaction") Transaction transaction) { transaction.setRiskLevel(RiskLevel.HIGH); System.out.println("交易金额过大或跨境,需要人工审核" ); } } @Rule(name = "可疑行为检测", priority = 2) public class SuspiciousBehaviorRule { @Condition public boolean isSuspicious (@Fact("user") User user, @Fact("transaction") Transaction transaction) { return user.getLoginLocation().equals("异地" ) && transaction.getAmount() > 5000 ; } @Action public void triggerAlert () { alertService.sendAlert("检测到可疑交易行为" ); } }
营销与促销系统
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 27 28 29 30 31 32 33 @Rule(name = "新用户优惠", priority = 1) public class NewUserDiscountRule { @Condition public boolean isNewUser (@Fact("user") User user, @Fact("order") Order order) { return user.isNewUser() && order.getAmount() > 100 ; } @Action public void applyDiscount (@Fact("order") Order order) { order.applyDiscount(0.1 ); System.out.println("新用户享受9折优惠" ); } } @Rule(name = "会员日促销", priority = 2) public class MemberDayRule { @Condition public boolean isMemberDay (@Fact("date") LocalDate date, @Fact("user") User user) { return date.getDayOfMonth() == 8 && user.isVIP(); } @Action public void applyMemberDiscount (@Fact("order") Order order) { order.applyDiscount(0.15 ); System.out.println("会员日享受85折优惠" ); } }
审批工作流系统
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 27 28 29 public class LoanApprovalWorkflow { @Rule(name = "基础资格检查", priority = 1) public class BasicEligibilityRule { @Condition public boolean checkBasic (@Fact("application") LoanApplication app) { return app.getAge() >= 22 && app.getAge() <= 60 ; } @Action public void markEligible (@Fact("result") ApprovalResult result) { result.addQualification("年龄符合要求" ); } } @Rule(name = "收入要求检查", priority = 2) public class IncomeRequirementRule { @Condition public boolean checkIncome (@Fact("application") LoanApplication app) { return app.getMonthlyIncome() >= app.getLoanAmount() * 0.3 ; } @Action public void markIncomeQualified (@Fact("result") ApprovalResult result) { result.addQualification("收入符合要求" ); } } }
智能路由与推荐系统
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 27 28 29 30 31 32 @Rule(name = "技术问题路由", priority = 1) public class TechnicalIssueRouteRule { @Condition public boolean isTechnicalIssue (@Fact("ticket") SupportTicket ticket) { return ticket.getCategory().equals("技术问题" ) || ticket.getKeywords().contains("bug" ); } @Action public void routeToTechTeam (@Fact("ticket") SupportTicket ticket) { ticket.assignTo("技术支持团队" ); System.out.println("技术问题已路由到技术支持团队" ); } } @Rule(name = "VIP客户优先路由", priority = 2) public class VIPCustomerRouteRule { @Condition public boolean isVIPCustomer (@Fact("user") User user, @Fact("ticket") SupportTicket ticket) { return user.isVIP() && ticket.getPriority() == Priority.HIGH; } @Action public void routeToSeniorStaff (@Fact("ticket") SupportTicket ticket) { ticket.assignTo("高级客服专员" ); System.out.println("VIP客户问题已路由到高级专员" ); } }
数据验证与清洗系统
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 27 28 29 30 31 32 @Rule(name = "数据完整性检查", priority = 1) public class DataCompletenessRule { @Condition public boolean hasMissingFields (@Fact("data") DataRecord record) { return record.getField("name" ) == null || record.getField("email" ) == null ; } @Action public void flagIncomplete (@Fact("data") DataRecord record) { record.setStatus(DataStatus.INCOMPLETE); System.out.println("数据记录缺少必填字段" ); } } @Rule(name = "数据格式验证", priority = 2) public class DataFormatRule { @Condition public boolean hasFormatIssues (@Fact("data") DataRecord record) { String email = record.getField("email" ); return email != null && !isValidEmail(email); } @Action public void flagFormatError (@Fact("data") DataRecord record) { record.setStatus(DataStatus.FORMAT_ERROR); System.out.println("邮箱格式不正确" ); } }
物联网设备监控系统
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 27 28 29 30 @Rule(name = "温度异常告警", priority = 1) public class TemperatureAlertRule { @Condition public boolean isTemperatureHigh (@Fact("sensor") SensorData sensor) { return sensor.getTemperature() > 80 ; } @Action public void triggerTemperatureAlert (@Fact("sensor") SensorData sensor) { alertService.sendAlert("设备温度异常: " + sensor.getTemperature()); coolingSystem.activate(); } } @Rule(name = "设备离线检测", priority = 2) public class DeviceOfflineRule { @Condition public boolean isDeviceOffline (@Fact("device") Device device) { return System.currentTimeMillis() - device.getLastHeartbeat() > 300000 ; } @Action public void triggerOfflineAlert (@Fact("device") Device device) { alertService.sendAlert("设备离线: " + device.getId()); maintenanceTeam.notify(device); } }
医疗诊断辅助系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Rule(name = "高血压风险评估", priority = 1) public class HypertensionRiskRule { @Condition public boolean isHighRisk (@Fact("patient") Patient patient, @Fact("exam") MedicalExam exam) { return exam.getBloodPressure() > 140 && patient.getAge() > 50 && patient.hasFamilyHistory("高血压" ); } @Action public void recommendCheckup (@Fact("patient") Patient patient) { recommendationService.addRecommendation( patient, "建议进行24小时动态血压监测" ); } }
不适用场景
规则极其简单且稳定 - 简单的if-else足以应对
性能要求极高 - 规则引擎有一定性能开销
规则间高度耦合 - 难以拆分为独立规则的情况
实时性要求极高 - 纳秒级响应的场景
将瀑布流式的代码,转变成以组件为核心概念的代码结构,这种结构的好处是可以任意编排,组件与组件之间是解耦的,组件可以用脚本来定义,组件之间的流转全靠规则来驱动。
LiteFlow只做基于逻辑的流转,而不做基于角色任务的流转(工作流引擎)
LiteFlow适用于拥有复杂逻辑的业务,比如说价格引擎,下单流程等,这些业务往往都拥有很多步骤,这些步骤完全可以按照业务粒度拆分成一个个独立的组件,进行装配复用变更。使用LiteFlow,你会得到一个灵活度高,扩展性很强的系统。因为组件之间相互独立,也可以避免改一处而动全身的这样的风险。
特性
组件定义统一: 所有的逻辑都是组件,为所有的逻辑提供统一化的组件实现方式,小身材,大能量。
规则轻量: 基于规则文件来编排流程,学习规则入门只需要5分钟,一看即懂。
规则多样化: 规则支持xml、json、yml三种规则文件写法方式,喜欢哪种用哪个。
任意编排: 再复杂的逻辑过程,利用LiteFlow的规则,都是很容易做到的,看规则文件就能知道逻辑是如何运转的。
规则持久化: 框架原生支持把规则存储在标准结构化数据库,Nacos,Etcd,Zookeeper,Apollo,Redis。您也可以自己扩展,把规则存储在任何地方。
优雅热刷新机制: 规则变化,无需重启您的应用,即时改变应用的规则。高并发下不会因为刷新规则导致正在执行的规则有任何错乱。
支持广泛: 不管你的项目是不是基于Springboot,Spring还是任何其他java框架构建,LiteFlow都能游刃有余。
Springboot支持全面: 支持Springboot 2.X到最新的Springboot 3.X。
脚本语言支持: 可以定义脚本语言节点,支持Groovy,Java,Kotlin,Javascript,QLExpress,Python,Lua,Aviator。未来还会支持更多的脚本语言。
脚本和Java全打通: 所有脚本语言均可调用Java方法,甚至于可以引用任意的实例,在脚本中调用RPC也是支持的。
规则嵌套支持: 只要你想的出,你可以利用简单的表达式完成多重嵌套的复杂逻辑编排。
组件重试支持: 组件可以支持重试,每个组件均可自定义重试配置和指定异常。
上下文隔离机制: 可靠的上下文隔离机制,你无需担心高并发情况下的数据串流。
声明式组件支持: 你可以让你的任意类秒变组件。
详细的步骤信息: 你的链路如何执行的,每个组件耗时多少,报了什么错,一目了然。
稳定可靠: 历时2年多的迭代,在各大公司的核心系统上稳定运行。
性能卓越: 框架本身几乎不消耗额外性能,性能取决你的组件执行效率。
自带简单监控: 框架内自带一个命令行的监控,能够知道每个组件的运行耗时排行。
规则
支持很多规则,可参考 官网
串行
THEN(a, b, c, d);
SER(a, b, c, d);
THEN(a, b, THEN(c, d));
并行
WHEN(a, b, c);
PAR(a, b, c);
选择
SWITCH(a).to(b, c, d); #组件a,来选择执行b,c,d中的一个
SWITCH(x).TO(a, b, c).DEFAULT(y); #添加默认执行
条件
THEN(IF(x, a),b); #x为条件节点,为真的情况下,执行链路就为x->a->b,为假链路就为x->b
THEN(IF(x, a,b),c); # 三元表达式:x为条件节点,为真的情况下,执行链路就为x->a->c,为假链路就为x->b->c
IF(x, a).ELSE(b); 等于 IF(x, a, b); # 如果a为真 x->a,否则 x->b
循环
FOR(5).DO(THEN(a, b)); 表示把a->b这个链路固定循环了5次
WHILE(w).DO(THEN(a, b));
ITERATOR(x).DO(THEN(a, b));
FOR(f).DO(THEN(a, b)).BREAK©; c这个节点需要为布尔组件,返回一个布尔值,为true则退出循环
…
使用
引入依赖和配置
1 2 3 4 5 6 7 8 <dependency > <groupId > com.yomahub</groupId > <artifactId > liteflow-spring-boot-starter</artifactId > <version > 2.12.4.1</version > </dependency > # application.yml 中引入配置 liteflow.rule-source=config/flow.el.xml
编排规则:config/flow.el.xml
1 2 3 4 5 6 <?xml version="1.0" encoding="UTF-8" ?> <flow > <chain name ="chain1" > THEN(a, b, c); </chain > </flow >
实现规则
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 @Component("a") public class ACmp extends NodeComponent { @Override public void process () { } } @Component("b") public class BCmp extends NodeComponent { @Override public void process () { } } @Component("c") public class CCmp extends NodeComponent { @Override public void process () { } }
调用规则
1 2 3 4 5 6 7 8 9 10 11 @Component public class YourClass { @Resource private FlowExecutor flowExecutor; public void testConfig () { LiteflowResponse response = flowExecutor.execute2Resp("chain1" , "arg" ); } }
由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。
Drools 是用Java语言编写的开放源码规则引擎,基于Apache协议,RETE算法。
Drools是一个绝对重量级的规则引擎,很多像金融行业、电信行业的大公司都在使用它作为规则引擎。
特性
业务分析师无法独立完成规则配置:由于规则主体DSL是编程语言(Java, Groovy, Python),仍然需要开发工程师维护
规则规模变大以后也会变得不好维护,相对硬编码的优势便不复存在
规则的语法仅适合扁平的规则,对于嵌套条件语义(then里嵌套when…then子句)的规则只能将条件进行笛卡尔积组合以后进行配置,不利于维护。
原理
drools规则引擎由以下三部分构成:
执行过程如下:
API 继承关系:
规则体语法结构规则体是规则文件内容中的重要组成部分,是进行业务规则判断、处理业务结果的部分。
1 2 3 4 5 6 7 rule "ruleName" attributes when LHS then RHS end
rule:关键字,表示规则开始,参数为规则的唯一名称。
attributes:规则属性,是rule与when之间的参数,为可选项。
when:关键字,后面跟规则的条件部分。
LHS(Left Hand Side):是规则的条件部分的通用名称。它由零个或多个条件元素组成。如果LHS为空,则它将被视为始终为true的条件元素。
then:关键字,后面跟规则的结果部分。
RHS(Right Hand Side):是规则的后果或行动部分的通用名称。
end:关键字,表示一个规则结束。
使用
业务场景:消费者在图书商城购买图书,下单后需要在支付页面显示订单优惠后的价格。具体优惠规则如下:
规则编号
规则名称
描述
1
规则一
所购图书总价在100元以下的没有优惠
2
规则二
所购图书总价在100到200元的优惠20元
3
规则三
所购图书总价在200到300元的优惠50元
4
规则四
所购图书总价在300元以上的优惠100元
引入jar和配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <dependency > <groupId > org.drools</groupId > <artifactId > drools-compiler</artifactId > <version > 7.10.0.Final</version > </dependency > <?xml version="1.0" encoding="UTF-8" ?> <kmodule xmlns ="http://www.drools.org/xsd/kmodule" > <kbase name ="myKbase1" packages ="rules" default ="true" > <ksession name ="ksession-rule" default ="true" /> </kbase > </kmodule >
创建规则文件resources/rules/bookDiscount.drl(新建 Order 类,添加属性 originalPrice和realPrice)
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package book.discount import com.demo.framework.brms.entity.Order rule "book_discount_1" when $order: Order(originalPrice < 100 ) then $order.setRealPrice($order.getOriginalPrice()); System.out.println("成功匹配到规则一:所购图书总价在100元以下的没有优惠" ); end rule "book_discount_2" when $order: Order(originalPrice < 200 && originalPrice >= 100 ) then $order.setRealPrice($order.getOriginalPrice() - 20 ); System.out.println("成功匹配到规则二:所购图书总价在100到200元的优惠20元" ); end rule "book_discount_3" when $order: Order(originalPrice <= 300 && originalPrice >= 200 ) then $order.setRealPrice($order.getOriginalPrice() - 50 ); System.out.println("成功匹配到规则三:所购图书总价在200到300元的优惠50元" ); end rule "book_discount_4" when $order: Order(originalPrice >= 300 ) then $order.setRealPrice($order.getOriginalPrice() - 100 ); System.out.println("成功匹配到规则四:所购图书总价在300元以上的优惠100元" ); end rule "book_discount_order" when $order: Order(originalPrice >= 0 ) then $order.setRealPrice($order.getOriginalPrice() - 100 ); System.out.println("指定规则:所购图书都优惠100元" ); end
使用规则
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 27 28 29 30 package com.demo.framework.brms.entity;@Data public class Order { private Double originalPrice; private Double realPrice; } @Test public void test1 () { KieServices kieServices = KieServices.Factory.get(); KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer(); KieSession kieSession = kieClasspathContainer.newKieSession(); Order order = new Order (); order.setOriginalPrice(210D ); kieSession.insert(order); kieSession.fireAllRules(); kieSession.dispose(); System.out.println("优惠前原始价格:" + order.getOriginalPrice() + ",优惠后价格:" + order.getRealPrice()); }
一个轻量级的 Java 规则引擎,它提供了简单而强大的方式来定义和执行业务规则。它的设计理念是让规则管理变得简单直观。
特性
轻量级 - 不依赖其他框架,核心包很小
简单易用 - 基于注解的规则定义
灵活 - 支持多种规则定义方式
可组合 - 规则可以组合成规则集
易于测试 - 规则可以独立测试
原理
EasyRule 的实现原理可以分为几个关键部分:规则定义、规则引擎、事实管理和执行流程。
策略模式 - 不同的规则执行策略
观察者模式 - 监听器机制
组合模式 - 组合规则
模板方法模式 - 规则执行流程
工厂模式 - 规则创建
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 27 public class AnnotationRuleProxy implements Rule { private final Object rule; private final List<ConditionMethod> conditions; private final List<ActionMethod> actions; @Override public boolean evaluate (Facts facts) { for (ConditionMethod condition : conditions) { Object[] args = extractArguments(condition, facts); Boolean result = (Boolean) condition.getMethod().invoke(rule, args); if (!result) return false ; } return true ; } @Override public void execute (Facts facts) { for (ActionMethod action : actions) { Object[] args = extractArguments(action, facts); action.getMethod().invoke(rule, args); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class RulePriorityComparator implements Comparator <Rule> { @Override public int compare (Rule rule1, Rule rule2) { return Integer.compare(rule1.getPriority(), rule2.getPriority()); } } public List<Rule> sort (Rules rules) { List<Rule> ruleList = new ArrayList <>(); for (Rule rule : rules) { ruleList.add(rule); } Collections.sort(ruleList, new RulePriorityComparator ()); return ruleList; }
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 27 28 29 30 31 public class DefaultRulesEngine implements RulesEngine { private final RulesEngineParameters parameters; @Override public void fire (Rules rules, Facts facts) { List<Rule> ruleList = sort(rules); ruleList = applyRulePriorityThreshold(ruleList); for (Rule rule : ruleList) { if (parameters.isSkipOnFirstAppliedRule() && triggered) { break ; } if (rule.evaluate(facts)) { rule.execute(facts); triggered = true ; if (parameters.isSkipOnFirstAppliedRule()) { break ; } } } } }
使用
引入依赖和配置
1 2 3 4 5 <dependency > <groupId > org.jeasy</groupId > <artifactId > easy-rules-core</artifactId > <version > 4.1.0</version > </dependency >
规则引擎配置
1 2 3 4 5 6 7 8 RulesEngineParameters parameters = new RulesEngineParameters () .skipOnFirstAppliedRule(true ) .skipOnFirstFailedRule(false ) .skipOnFirstNonTriggeredRule(false ) .rulePriorityThreshold(10 ); RulesEngine rulesEngine = new DefaultRulesEngine (parameters);
定义多个规则
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Rule(name = "Adult Rule", description = "检查是否成年", priority = 1) public class AdultRule { @Condition public boolean isAdult (@Fact("age") int age) { return age >= 18 ; } @Action public void printAdult () { System.out.println("已成年,可以进入" ); } } @Rule(name = "Senior Rule", description = "检查是否是老年人", priority = 2) public class SeniorRule { @Condition public boolean isSenior (@Fact("age") int age) { return age >= 60 ; } @Action public void printSenior () { System.out.println("老年人,享受优惠" ); } } @Rule(name = "Child Rule", description = "检查是否是儿童", priority = 3) public class ChildRule { @Condition public boolean isChild (@Fact("age") int age) { return age < 18 ; } @Action public void printChild () { System.out.println("未成年人,需要监护人陪同" ); } }
组合使用规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) { RulesEngine rulesEngine = new DefaultRulesEngine (); Rules rules = new Rules (); rules.register(new AdultRule ()); rules.register(new SeniorRule ()); rules.register(new ChildRule ()); Facts facts = new Facts (); facts.put("age" , 25 ); rulesEngine.fire(rules, facts); }