规则引擎技术解决方案

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 规则引擎技术选型

对比内容 Drools easyRules QlExpress Apache Camel
项目背景 JBoss                 阿里 Apache
SpringBoot整合  V V 支持Spring 支持Spring
平台支持 Java Java Java Java
热更新 V X X /
维护 一般 /
LHS规则条件 V V V /
RHSthen部分触发执行、业务处理 V V V /
活跃度 非常活跃的社区支持;star 4.6k/last update 2023.8.2/commits 16898 star 4.5k/last update 2020.12.7/commits 659 star 4.2k/last update 2023.2.6/commits 309 star 4.9k/last update aways/commits 309
性能 良好 高性能 /
学习成本 引擎设计和实现都比较复杂,学习成本高,适用于大型应用系统 方便且适用于Java的抽象的业务模型规则,轻量级类库和容易上手 需要一定的学习成本 需要一定的学习成本
免费开源 V V V V
复杂业务场景的支持 复杂规则支持 简单规则。基于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导包


     org.drools
    drools-core
    7.57.0.Final


    org.drools
    drools-compiler
    7.57.0.Final


    org.kie
    kie-api
    7.57.0.Final


    org.drools
    drools-mvel
    7.57.0.Final

Idea安装Drools插件。

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



    
    
        
        
        
    

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个月");
end

rule "crime_2" extends "crime_1"
when
    Judge(crime.contains("抢劫"));
then
    $judge.setSentence(24);
    System.out.println("触发猥亵和抢劫, 2年");
end

rule "crime_3" extends "crime_2"
when
    Judge(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 {
    @Test
    public 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();
        // 第六步:关闭session
        kieSession.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导包


   org.jeasy
   easy-rules-core
   4.1.0


   org.jeasy
   easy-rules-support
   4.1.0


    org.jeasy
    easy-rules-mvel
    4.1.0

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 {
    @Test
    public 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导包


   com.alibaba
   QLExpress
   3.3.1

6.2编写测试用例

/**
 * @author jingyan
 * @createTime 2023年08月04日 18:04:00
 * @Description 补充QLExpress表达式规则引擎
 */
public class TestQLExpress {
    @Test
    public void test() throws Exception {
        ExpressRunner runner = new ExpressRunner();
        DefaultContext 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 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 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和目标中获取数据,我们希望将数据发送到这些数据源。

你可能感兴趣的:(架构落地手记,Java开发框架与工具,java)