当前位置: 首页 > news >正文

规则引擎技术解决方案

1 概述

1.1 规则引擎的背景

业务系统在应用过程中,常常包含着要处理“复杂、多变”的部分,这部分往往是“业务规则”或者是“数据的处理逻辑”。因此这部分的动态规则的问题,往往需要可配置,并对系统性能和热部署有一定的要求。从开发与业务的视角主要突出以下的一些问题:

1.1.1从开发人员视角来看

1)逻辑复杂,要使用大量if-else、switch-case来实现,或者使用设计模式。 但过于复杂的规则逻辑,使用设计模式也往往是存在大量并且关系复杂 的类,导致代码难于维护。

2)变更时需要从头梳理逻辑,在适当的地方进行if…else…、switch-case代码 逻辑调整,耗费大量时间进行梳理。

3)开发周期较长,当需求发生变更时,需要研发人员安排开发周期上线, 对于当下快速变化的业务,传统的开发工作方式显得捉襟见肘。

1.1.2从业务人员视角来看

1)业务人员期望友好的管理界面,不需要专业的开发技能就能够完成规则 的管理、发布。

2)期望能够实现热部署,由业务人员配置好之后即配即用。

3)减少业务规则对开发人员的依赖。

4)降低需求变动的时间成本,快速验证发布。

1.2 规则引擎介绍

规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。

规则本质上是一个函数,如y=f(x1,x2,….xn)。

规则引擎有三部分:

  1. 事实(Fact):就是用户驶入的已经事实,即已知对象。
  2. LHS(Left Hand Side):规则执行需要满足的条件。
  3. RHS(Right Hand Sike):规则执行后的返回对象。

两个重要模块:

  1. 规则管理:主要涉及规则、实施对象和规则集三个实体。涉及到规则变更时,最好对规则加版本,可通过规则版本控制,可以平滑灰度地改变规则,也便于测试规则的正确性。
  2. 规则执行:通过规则库数据,通过规则引擎的规则解析、规则编译,将可执行代码缓存起来。也可根据需求,不依赖规则库的存储方式,如选用Drools等第三方引擎,甚至基于ANTLR定制。

1.3 规则引擎使用场景

◆ 使用规则引擎的好处

■ 声明式编程:规则引擎允许你描述做什么而不是如何去做

■ 业务规则与系统代码分离,实现业务规则的集中管理。数据保存在系统对象中,逻辑保存在规则中。

■ 速度及可测量性:Rete算法、Leaps算法,以及由此衍生出来的Drools的Rete、Leaps算法,提供了对系统数据对象非常有效率的匹配

■ 知识集中化:通过使用规则,将建立一个可执行的规则库。这意味着规则库代表着现实中的业务策略的唯一对应,理想情况下可读性高的规则还可以被当作文档使用

■ 工具集成:例如Eclipse(将来可能在基于Web的界面上)这样的工具为规则的修改与管理、即时获得反馈、内容验证与修补提供了办法。审查与调试工具同样也可用了

■ 解释机制:通过将规则引擎的决断与决断的原因一起记录下来,规则系统提供了很好的“解释机制”

■ 易懂的规则:通过建立对象模型以及DSL(域定义语言),可以用接近自然语言的方式来编写规则。规则引擎是相对独立的,只关心业务规则,使得业务分析⼈员、非技术人员与领域专家也可以用他们自己的逻辑来理解规则,参与编辑、维护系统的业务规则

   ■  在不重启服务的情况下可随时对业务规则进行扩展和维护

   ■ 可以动态修改业务规则,从⽽快速响应需求变更

   ■ 减少了硬编码业务规则的成本和风险

   ■ 使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单 

◆ 适合使用规则引擎系统的场景

  ■ 用传统的代码开发比较复杂、繁琐

■ 问题虽然不复杂,但是用传统的代码开发比较脆弱,也就是经常修改

■ 没有优雅的算法

■ 业务规则频繁改变

■ 有很多业务专家

如:

  • 风险控制系统----风险贷款、风险评估
  • 反欺诈项目----银行贷款、征信验证
  • 决策平台系统----财务计算
  • 促销平台系统----满减、打折、加价购
  • 手机运营商资费套餐
  • 超市、商场,商城等等积分计算规则
  • 寿险车险理赔
  • 工资计算(ScriptEngine)
  • 决策平台系统----业务决策判断
  •  PS:如果应用的生命周期很短,也没有必要使用Drools,使用规则引擎将会在中长期维护中得到好处。

◆ 不适合使用规则引擎系统的场景

虽然规则系统看起来比较不错,但是并不是任何地方都可以使用规则系统。

■ 很多简单、固定的业务系统,可以不用使用规则系统

■ 问题虽然不复杂,但是用传统的代码开发比较脆弱,也就是经常修改

■ 没有优雅的算法

■ 业务规则频繁改变

■ 开发时间紧、任务急、工作量大

2 总体设计方案

暂无。

3 规则引擎技术选型

对比内容DroolseasyRulesQlExpressApache Camel
项目背景JBoss                阿里Apache
SpringBoot整合 VV支持Spring支持Spring
平台支持JavaJavaJavaJava
热更新VXX/
维护一般/
LHS规则条件VVV/
RHSthen部分触发执行、业务处理VVV/
活跃度非常活跃的社区支持;star 4.6k/last update 2023.8.2/commits 16898star 4.5k/last update 2020.12.7/commits 659star 4.2k/last update 2023.2.6/commits 309star 4.9k/last update aways/commits 309
性能良好高性能/
学习成本引擎设计和实现都比较复杂,学习成本高,适用于大型应用系统方便且适用于Java的抽象的业务模型规则,轻量级类库和容易上手需要一定的学习成本需要一定的学习成本
免费开源VVVV
复杂业务场景的支持复杂规则支持简单规则。基于MVEL表达式的编程模型(适用于极简单的规则,一般不推荐)复杂规则支持/
技术成熟度一般一般
规则引擎类型业务规则引擎业务规则引擎业务规则引擎,表达式路由和中介规则引擎。适用于集成/传输层。在系统之间映射消息格式,转换协议(JMS,HTTP,FTP等)和消息标准(如XML,JSON等)以及路由

3.1目前业务特点选型

法律法规业务规则、表达式(布尔组合)、语义语法分析等强业务需求,由业务人员将历年法律法规规则按照规则编写xml文档,由后端读取XML规则内容,通过对法律案例进行分词、匹配、正则表达式、语义关联分析等与读取的XML规则内容比对、匹配,实现对法律案例的关键字的提取,从而获取相应的结果。根据实际情况,先采用硬编码读取XML文件进行对text内容比对、匹配等方式完成任务。后期通过分享Drools、easyRule的方式实现业务和技术的分离,争取考虑使用页面配置规则存库、读取规则、匹配规则方式,形成公司自有的规则库。

4.Drools简介及使用

Drools是⼀款由JBoss组织提供的基于Java语⾔开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在⽂件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、不⽤重启服务器就可以在线上环境立即生效。

 Drools官网地址:https://drools.org/

​ drools源码下载地址:https://github.com/kiegroup/drools

使用 Drools 需要将原有的代码抽象成:Rule(规则) + Fact(事实)。

在Java应用中使用Drools 规则引擎的步骤如下:

1.准备规则:创建规则文件并定义规则。

2.构建Kie容器:使用KieServices接口创建Kie容器。

3.获取KieBase:使用KieServices.newKieClasspathContainer()方法获取KieBase。

4.创建KieSession:使用KieBase.newKieSession)方法创建KieSession。

5.插入数据:使用KieSession.insert()方法插入数据。

6.执行规则:使用KieSession.fireAllRules()方法执行规则。

7.关闭KieSession:使用KieSession.dispose()方法关闭KieSession。

4.1 pom导包

<dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>7.57.0.Final</version>
</dependency>
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.57.0.Final</version>
</dependency>
<dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>7.57.0.Final</version>
</dependency>
<dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>7.57.0.Final</version>
</dependency>

Idea安装Drools插件。

4.2 resource下新建META-INF文件夹,新建并编写kmodule.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule"><!--name:指定kbase 名称,可以是任意但必须唯一packages:指定规则文件存放目录,依据实际情况进行填写default:指定当前的kbase 是否为默认--><kbase name="rules" packages="rules" default="true"><!--name:指定ksession名称,可以是任意但必须唯一default:指定ksession 是否为默认--><ksession name="ksession-rules" default="true"/><!--<ksession name="name-rules" default="true"/>--></kbase>
</kmodule>

4.3 resource下新建rules文件夹,新建并编写crime-rules.drl文件

package rules;import com.example.testdemo.Judge;rule "crime_1"
when$judge:Judge(crime.contains("猥亵"));
then$judge.setSentence(3);System.out.println("触发猥亵,3个月");
endrule "crime_2" extends "crime_1"
whenJudge(crime.contains("抢劫"));
then$judge.setSentence(24);System.out.println("触发猥亵和抢劫, 2年");
endrule "crime_3" extends "crime_2"
whenJudge(crime.contains("杀人未遂"));
then$judge.setSentence(120);System.out.println("触发猥亵、抢劫、杀人未遂, 10年");
end

4.4 编写测试用例

/*** @author jingyan* @createTime 2019年07月04日 13:51:00* @Description*/
public class TestDrools {@Testpublic void test1() {// 第一步KieServices kieServices = KieServices.Factory.get();// 第二步KieContainer kieContainer = kieServices.getKieClasspathContainer();// 第三步KieSession kieSession = kieContainer.newKieSession("ksession-rules");// 业务对象Judge judge = new Judge();judge.setCrime("嫌疑人xxx,于2012年因猥亵、抢劫、杀人未遂,数案并罚");// 第四步kieSession.insert(judge);// 第五步:执行规则引擎kieSession.fireAllRules();// 第六步:关闭sessionkieSession.dispose();System.out.println("指定规则引擎后的结果:" + judge.getSentence());}
}

运行结果:

14:52:07.483 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: rules
14:52:07.535 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
触发猥亵,3个月
触发猥亵和抢劫, 2年
触发猥亵、抢劫、杀人未遂, 10年
14:52:07.571 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
14:52:07.571 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE
14:52:07.571 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED
指定规则引擎后的结果:120

4.5Drools数据库表结构设计

-- ----------------------------
-- Table structure for drools_biz_rule
-- ----------------------------
DROP TABLE IF EXISTS `drools_biz_rule`;
CREATE TABLE `drools_biz_rule`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`rule_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '规则标识',`rule_condition` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '规则数据',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `uni_code`(`rule_code`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

5.EasyRule简介及使用

     easy-rules是一款轻量级的java规则引擎。支持快速简单的从yml、json文件中加载Rule描述文件。EasyRule是Drools的简化版,裁剪了部分使用场景非常少的复杂功能,也简化了很多Drools中的组件。

官网:https://github.com/j-easy/easy-rules/

5.1框架特点

  • 轻量级类库
  • 容易学习的API
  • 基于POJO的注解编程模型开发
  • 通过高效的抽象来定义业务规则并易于用JAVA应用它们
  • 支持通过原始规则创建复合规则
  • 支持用表达式语言定义规则

5.2功能组件

  • 事实(Fact):业务数据,结构类似HashMap;
  • 规则(Rule):业务规则,包含条件评估、动作执行,条件评估结果为true,则执行对应动作;
  • 规则引擎(Rule Engine):以指定的方式执行规则;
  • 规则监听(Rule Listener):监听规则的执行情况;
  • 规则引擎监听(Rule Engine Listener):监听规则引擎的执行情况。

5.3EasyRule使用

5.3.1 pom导包

<dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-core</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-support</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-mvel</artifactId><version>4.1.0</version>
</dependency>

5.3.2 resource下新建easyrules文件夹,新建并编写rule.yml文件

---
name: "crime_1"
description: "猥亵罪"
#priority: 1
condition: "judge.crime.contains(\"猥亵\")"
actions:- "judge.setSentence(3)"- "System.out.println(\"触发猥亵,3个月\")"---
name: "crime_2"
description: "猥亵和抢劫罪并罚"
#priority: 2
condition: "judge.crime.contains(\"猥亵\")&&judge.crime.contains(\"抢劫\")"
actions:- "judge.setSentence(24)"- "System.out.println(\"触发猥亵和抢劫, 2年\")"---
name: "crime_3"
description: "猥亵、抢劫罪和杀人未遂并罚"
#priority: 0
condition: "judge.crime.contains(\"猥亵\")&&judge.crime.contains(\"抢劫\")&&judge.crime.contains(\"杀人未遂\")"
actions:- "judge.setSentence(120)"- "System.out.println(\"触发猥亵、抢劫、杀人未遂, 10年\")"

5.3.3.4 编写测试用例

/*** @author jingyan* @createTime 2019年07月04日 15:54:00* @Description*/
public class TestEasyRule {@Testpublic void test() throws Exception {// 创建规则引擎RulesEngineParameters parameters = new RulesEngineParameters()//优先级超过定义的阈值,则跳过下一个规则//.priorityThreshold(10)//规则被触发时跳过后面的规则.skipOnFirstAppliedRule(false)//规则失败时跳过后面的规则.skipOnFirstFailedRule(true)//一个规则不会被触发跳过后面的规则.skipOnFirstNonTriggeredRule(false);RulesEngine engine = new DefaultRulesEngine(parameters);// 创建规则String filePath = System.getProperty("user.dir") + "/src/main/resources/easyrules/rule.yml";MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());Rules yamlRules = ruleFactory.createRules(new FileReader(filePath));Judge judge = new Judge();judge.setCrime("嫌疑人xxx,于2012年因猥亵、抢劫、杀人未遂,数案并罚");// 执行规则Facts facts = new Facts();facts.put("judge", judge);engine.fire(yamlRules, facts);System.out.println("指定规则引擎后的结果:" + JSON.toJSONString(judge));}
}

运行结果:

17:12:28.204 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Fact{name='judge', value=Judge(crime=嫌疑人xxx,于2012年因猥亵、抢劫、杀人未遂,数案并罚, sentence=0)}
17:12:28.204 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rules evaluation started
17:12:28.229 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'crime_1' triggered
触发猥亵,3个月
17:12:28.232 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'crime_1' performed successfully
17:12:28.233 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'crime_2' triggered
触发猥亵和抢劫, 2年
17:12:28.233 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'crime_2' performed successfully
17:12:28.234 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'crime_3' triggered
触发猥亵、抢劫、杀人未遂, 10年
17:12:28.234 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'crime_3' performed successfully
指定规则引擎后的结果:{"crime":"嫌疑人xxx,于2012年因猥亵、抢劫、杀人未遂,数案并罚","sentence":120}

5.3.3.5EasyRule数据库表结构设计

-- ----------------------------
-- Table structure for easy_biz_rule
-- ----------------------------
DROP TABLE IF EXISTS `easy_biz_rule`;
CREATE TABLE `easy_biz_rule` (`id` bigint(22) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '规则名称',`description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则描述',`priority` int(11) DEFAULT NULL COMMENT '权重',`composite_type` tinyint(4) DEFAULT NULL COMMENT '组合类型 1-and 2-or 3-all',`state` tinyint(4) DEFAULT NULL COMMENT '数据状态 0-有效 1-无效',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`) USING BTREE,KEY `idx_name` (`name`) USING BTREE COMMENT '策略名称'
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;-- ----------------------------
-- Table structure for easy_biz_compose
-- ----------------------------
DROP TABLE IF EXISTS `easy_biz_compose`;
CREATE TABLE `easy_biz_compose` (`id` bigint(22) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',`rule_id` bigint(22) NOT NULL COMMENT '规则ID',`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则名称',`description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则描述',`condition` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则条件',`actions` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '执行动作',`priority` int(11) DEFAULT NULL COMMENT '规则权重',`state` tinyint(4) DEFAULT NULL COMMENT '数据状态 0-有效 1-无效',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`) USING BTREE,KEY `idx_rule` (`rule_id`) USING BTREE COMMENT '规则'
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;

6.QLExpress简介和使用

由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。让业务人员就可以定义业务规则。支持标准的JAVA语法,还可以支持自定义操作符号、操作符号重载、函数定义、宏定义、数据延迟加载等。

QLExpress脚本引擎被广泛应用在阿里的电商业务场景,具有以下的一些特性:

  • 1、线程安全,引擎运算过程中的产生的临时变量都是threadlocal类型。
  • 2、高效执行,比较耗时的脚本编译过程可以缓存在本地机器,运行时的临时变量创建采用了缓冲池的技术,和groovy性能相当。
  • 3、弱类型脚本语言,和groovy,javascript语法类似,虽然比强类型脚本语言要慢一些,但是使业务的灵活度大大增强。
  • 4、安全控制,可以通过设置相关运行参数,预防死循环、高危系统api调用等情况。
  • 5、代码精简,依赖最小,250k的jar包适合所有java的运行环境,在android系统的低端pos机也得到广泛运用。

官网:https://github.com/alibaba/QLExpress

6.1 pom导包

<dependency><groupId>com.alibaba</groupId><artifactId>QLExpress</artifactId><version>3.3.1</version>
</dependency>

6.2编写测试用例

/*** @author jingyan* @createTime 2023年08月04日 18:04:00* @Description 补充QLExpress表达式规则引擎*/
public class TestQLExpress {@Testpublic void test() throws Exception {ExpressRunner runner = new ExpressRunner();DefaultContext<String, Object> context = new DefaultContext<>();context.put("a", 1);context.put("b", 2);context.put("c", 3);String express = "a + b * c";Object r = runner.execute(express, context, null, true, false);System.out.println(r);runner.addOperatorWithAlias("如果", "if", null);runner.addOperatorWithAlias("则", "then", null);runner.addOperatorWithAlias("否则", "else", null);express = "如果 (语文 + 数学 + 英语 > 270) 则 {return 1;} 否则 {return 0;}";Object o = runner.execute(express, context, null, false, false, 50);System.out.println(o);/*runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs", new String[] {"double"}, null);runner.addFunctionOfClassMethod("转换为大写", BeanExample.class.getName(), "upper", new String[] {"String"}, null);runner.addFunctionOfServiceMethod("打印", System.out, "println", new String[] { "String" }, null);runner.addFunctionOfServiceMethod("contains", new BeanExample(), "anyContains", new Class[] {String.class, String.class}, null);String express1 = "取绝对值(-100); 转换为大写(\"hello world\"); 打印(\"你好吗?\"); contains(\"helloworld\",\"aeiou\")";Object e = runner.execute(express1, context, null, false, false);System.out.println(e);*/}
}

运行结果:

Connected to the target VM, address: '127.0.0.1:11215', transport: 'socket'
7
1
Disconnected from the target VM, address: '127.0.0.1:11215', transport: 'socket'

6.3resource下新建qLrules文件夹,新建并编写rule.ql文件

是否符合 = 1;
运费 = 0;
最长边 = 长 > 宽 ? 长 : 宽;
最长边 = 最长边 > 高 ? 最长边 : 高;
最短边 = 长 < 宽 ? 长 : 宽;
最短边 = 最短边 < 高 ? 最短边 : 高;
中间边 = (长 + 宽 + 高) - 最长边 - 最短边;
围长 = (中间边 + 最短边) * 2 + 最长边;
如果 (围长 > 300) 则{是否符合 = 0;return 是否符合;
}
如果 (最长边 > 175) 则{是否符合 = 0;return 是否符合;
}
如果 (30 >= 重量 ) 则{是否符合 = 0;return 是否符合;
}
如果 (30 < 重量 ) 则{是否符合 = 0;运费 = 1.5*(重量-30);return 是否符合;
}

6.4编写测试用例

@Test
public void ruleTest() throws Exception {List<String> ruleFileNames = new ArrayList<>();ruleFileNames.add("qLrules/rule.ql");for (int i = 0; i < ruleFileNames.size(); i++) {String script = getResourceAsStream(ruleFileNames.get(i));ExpressRunner runner = new ExpressRunner(false, false);runner.addOperatorWithAlias("如果", "if", null);runner.addOperatorWithAlias("则", "then", null);runner.addOperatorWithAlias("否则", "else", null);IExpressContext<String, Object> context = new DefaultContext<>();try {context.put("长", 2);context.put("宽", 20);context.put("高", 40);context.put("重量", 35);context.put("COUNTRY","IS");runner.execute(script, context, null, true, false);System.out.println("文件名称:" + ruleFileNames.get(i));System.out.println("最长边:" + context.get("最长边"));System.out.println("中间边:" + context.get("中间边"));System.out.println("最短边:" + context.get("最短边"));System.out.println("是否符合:" + context.get("是否符合"));System.out.println("运费:" + context.get("运费"));} catch (Exception e) {e.printStackTrace();//Assert.assertTrue(e.toString().contains("at line 7"));}}
}

运行结果:

Connected to the target VM, address: '127.0.0.1:13783', transport: 'socket'
文件名称:qLrules/rule.ql
最长边:40
中间边:20
最短边:2
是否符合:0
运费:7.5
Disconnected from the target VM, address: '127.0.0.1:13783', transport: 'socket'

7.Apache Camel简介

Apache Camel是Apache基金会下的一个开源项目,它是一个基于规则路由和中介的规则引擎。包括基于Java的Fluent API,Spring或者Blueprint XML配置文件,甚至是Scala(是一种基于JVM,集合了面向对象编程和函数式编程优点的高级程序设计语言)DSL。 能够通过IDE或者Java、Scala或者XML编辑器里获得智能化路由规则补全功能。

Apache Camel可以做到:

路由:将数据有效负载(也称为“消息”)从源系统发送到目标系统。from().to().to()。

中介:消息处理,如基于一个或多个消息属性过滤消息、修改消息的某些字段、通过API调用进行充实等。

在面向服务的体系结构的项目中,Camel通常与Apache ServiceMix, Apache ActiveMQ以及Apache CXF一同使用。

◆ 适合使用Apache Camel的场景:

  • 具有丰富组件集的Apache Camel对于需要通过不同协议(如文件、api或JMS队列)与系统集成的应用程序非常有用。
  • Apache Camel的实现了企业集成模式,满足非常复杂的集成场景。
  • 微服务中的编排和编排可以用Apache Camel路由中的领域特定语言来定义。路由有助于保持核心业务逻辑与通信逻辑解耦,并满足SRP(单一责任原则)。
  • Apache Camel非常适合Java和Spring应用程序。
  • 使用Java对象(pojo): Apache Camel是一个Java框架,因此它特别擅长使用Java对象。因此,如果使用的是XML、JSON等文件格式,可以反序列化为Java对象,那么Camel就可以轻松地对其进行处理。

◆ 不适合使用Apache Camel的场景:

  • 如果我们的集成只是简单调用少量api
  • Camel在处理大量数据时表现不佳
  • Camel也不适合缺乏Java技能的团队

一般来说,camel的最佳用例是,有一个数据源,我们希望从其中消费数据,比如队列上的传入消息,或者从API和目标中获取数据,我们希望将数据发送到这些数据源。

相关文章:

规则引擎技术解决方案

1 概述 1.1 规则引擎的背景 业务系统在应用过程中&#xff0c;常常包含着要处理“复杂、多变”的部分&#xff0c;这部分往往是“业务规则”或者是“数据的处理逻辑”。因此这部分的动态规则的问题&#xff0c;往往需要可配置&#xff0c;并对系统性能和热部署有一定的要求。从…...

2023奇安信天眼设备--面试题

1.在天眼分析平台网络协议中sip、dip、sport、dport字段表示的含义是什么&#xff1f; sip 源IP、dip 目的IP、sport 源端口、dport 目的端口 2.在天眼分析平台DNS协议中dns type字段表示的含义是? dns type表示DNS请求类型 0代表DNS请求&#xff0c;1代表DNS响应 3.dns_typ…...

【剑指Offer 58】 左旋转字符串,Java解密。

LeetCode 剑指Offer 75道练习题 文章目录 剑指Offer:左旋转字符串示例:限制:解题思路:剑指Offer:左旋转字符串 【题目描述】 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdef…...

Python SMTP发送邮件

Python SMTP发送邮件 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则&#xff0c;由它来控制信件的中转方式。 python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的…...

Jmeter-获取接口响应头(Response headers)信息进行关联

文章目录 Jmeter-获取接口响应头&#xff08;Response headers&#xff09;信息进行关联使用正则表达式提取器将Set-Cookie的值提取出来在其余接口中关联该提取信息运行查看关联是否成功 Jmeter-获取接口响应头&#xff08;Response headers&#xff09;信息进行关联 获取某一…...

解密爬虫ip是如何被识别屏蔽的

在当今信息化的时代&#xff0c;网络爬虫已经成为许多企业、学术机构和个人不可或缺的工具。然而&#xff0c;随着网站安全防护的升级&#xff0c;爬虫ip往往容易被识别并屏蔽&#xff0c;给爬虫工作增加了许多困扰。在这里&#xff0c;作为一家专业的爬虫ip供应商&#xff0c;…...

GPIO实验

一、GPIO GPIO&#xff08;General-purpose input/output&#xff09;即通用型输入输出&#xff0c;GPIO可以控制连接在其之上的引脚实现信号的输入和输出 芯片的引脚与外部设备相连&#xff0c;从而实现与外部硬件设备的通讯、控制及信号采集等功能 LED实验步骤 最终目的&am…...

Docker-Compose编排与部署(lnmp实例)

第四阶段 时 间&#xff1a;2023年8月3日 参加人&#xff1a;全班人员 内 容&#xff1a; Docker-Compose编排与部署 目录 一、Docker Compose &#xff08;一&#xff09;概述 &#xff08;二&#xff09;Compose适用于所有环境&#xff1a; &#xff08;三&#xf…...

Docker 网络模型使用详解 (1)Dockers网络基础

目录 环境准备 Dockers 网络基础 1.端口映射 查看随机映射端口范围 -p可以指定映射到本地端口 映射指定地址和指定端口 映射指定地址 宿主机端口随机分配 指定传输协议 端口暴露 容器互联 自定义网络 现在把container7加入到demo_net中 在启动一个容器加入到demo_net…...

【Spring】(四)Bean 的作用域和生命周期

文章目录 前言一、Bean 的作用域1.1 被修改的 Bean 案例1.2 作用域的定义1.3 Bean 的六种作用域1.4 Bean 作用域的设置 二、Spring 的执行流程 和 Bean 的生命周期2.1 Spring 的执行流程2.2 Bean 的生命周期2.3 Bean 生命周期的演示 前言 Bean 是 Spring 框架中的一个核心概念…...

卷积神经网络【图解CNN】

文章目录 1.卷积运算2.池化3.全连接层 卷积神经网络可以看作一个函数或者黑箱&#xff0c;输入就是图片的像素阵列&#xff0c;输出就是这个图片是什么&#xff1f; 图片是X&#xff0c;那么就输出‘x’&#xff0c;图片是‘O’,那么就输出O&#xff1b; 在计算机眼中&#xff…...

命令模式 Command Pattern 《游戏设计模式》学习笔记

对于一般的按键输入&#xff0c;我们通常这么做&#xff0c;直接if按了什么键&#xff0c;就执行相应的操作 在这里我们是将用户的输入和程序行为硬编码在一起&#xff0c;这是我们很自然就想到的最快的做法。 但是如果这是一个大型游戏&#xff0c;往往我们需要实现一个按键…...

供水管网漏损监测,24小时保障城市供水安全

供水管网作为城市生命线重要组成部分&#xff0c;其安全运行是城市建设和人民生活的基本保障。随着我国社会经济的快速发展和城市化进程的加快&#xff0c;城市供水管网的建设规模日益增长。然而&#xff0c;由于管网老化、外力破坏和不当维护等因素导致的供水管网漏损&#xf…...

How to Use Glslang

文章目录 Execution of Standalone Wrapper构建 (CMake)依赖关系构建步骤如果需要更改 GLSL 语法测试运行测试基本内部操作 Execution of Standalone Wrapper 要使用独立的二进制形式&#xff0c;请执行glslang&#xff0c;它将打印一条使用语句。基本操作是给它一个包含着色器…...

AcWing 24:机器人的运动范围 ← BFS、DFS

【题目来源】https://www.acwing.com/problem/content/description/22/【题目描述】 地上有一个 m 行和 n 列的方格&#xff0c;横纵坐标范围分别是 0∼m−1 和 0∼n−1。 一个机器人从坐标 (0,0) 的格子开始移动&#xff0c;每一次只能向左&#xff0c;右&#xff0c;上&#…...

RF手机天线仿真介绍(一):金属边框天线和LDS天线

目录 简介LDS天线LDS天线仿真 金属边框天线金属边框天线仿真 简介 最早的手机是外置式天线&#xff0c;从NOKIA开始采用内置式天线&#xff0c;开始采用内置金属片&#xff08;一般是0.1MM厚的不锈钢片冲压而成&#xff09;&#xff0c;随后为降低成本&#xff0c;后来改用FPC…...

动手学深度学习—深度学习计算(层和块、参数管理、自定义层和读写文件)

目录 1. 层和块1.1 自定义块1.2 顺序块1.3 在前向传播函数中执行代码 2. 参数管理2.1 参数访问2.1.1 目标参数2.1.2 一次性访问所有参数2.1.3 从嵌套块收集参数 2.2 参数初始化2.2.1 内置初始化2.2.2 自定义初始化 2.3 参数绑定 3. 自定义层3.1 不带参数的层3.2 带参数的层 4. …...

Pytest学习教程_测试报告生成pytest-html(三)

前言 pytest-html 是一个用于生成漂亮的 HTML 测试报告的 pytest 插件。它可以方便地将 pytest 运行的测试结果转换为易于阅读和理解的 HTML 报告&#xff0c;提供了丰富的测试结果展示功能和交互性。 一、安装 # 版本查看命令 pytest版本&#xff1a; pytest --version pyte…...

模块化原理:source-map

1. webpack打包基本配置 1.安装webpack与webpack-cli npm i webpack webpack-cli 2.配置 "build":"webpack" 3. 新建webpack.config.js const path require(path); module.exports {// mode: "development",// 默认production&#xff08;什么…...

【C++】开源:ncurses终端TUI文本界面库

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍ncurses终端文本界面库。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】

1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...