一个简单的Drools规则引擎用例,可动态更新配置规则

本文介绍了一个基于SpringBoot和Drools的简单项目案例,演示了如何使用Drools规则引擎实现业务逻辑的动态配置及更新。项目包括执行规则引擎和动态更新规则两个主要功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个简单的Drools规则引擎用例,可动态更新配置规则

前言

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

本文描述的是一个简单的SpringBoot + Drools项目用例。用例中使用的是Drools规则引擎的无状态会话(StatelessKIESession)。为方便排版部分代码已经过删减,完整代码可到“项目地址”获取。

项目提供两个API接口:

  1. 执行规则引擎
  2. 动态更新规则

引入依赖

   <!-- Drools -->
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
        <version>7.4.1.Final</version>
    </dependency>

执行规则引擎

项目中默认提供两个drl规则文件,对应生成两个会话(单个会话可对应多个drl文件),会话为无状态会话,不使用推理,形成最简单的用例。

<kbase name="firstRules" packages="rules.first">
    <ksession name="firstSession" type="stateless"/>
</kbase>

<kbase name="secondRules" packages="rules.second">
    <ksession name="secondSession" type="stateless"/>
</kbase>

规则一: 传入Student对象,通过age匹配规则设置对象的message。drl文件中两个rule仅优先级不同,此文件测试global参数的使用与优先级测试。

rule "myTest two"
	dialect "java"
	no-loop true
	lock-on-active true
    activation-group "myTest"
    salience 30
	when
        std:Student(age > 10)
	then
        std.setMessage(str + ":priority-30");
end

规则二: 传入Student对象,通过age判断是否成年,并在name后添加备注。

rule "match two"
	dialect "java"
	no-loop true
	lock-on-active true
    activation-group "match"
    salience 20
	when
        std:Student(age >= 18);
	then
        std.setName(std.getName() + "-已成年");
end

执行规则引擎:传入参数age=18, name=‘小明’。

    @ApiOperation(value = "执行规则引擎")
    public BaseResponse<String> executeDrools() {
        BaseResponse<String> result = new BaseResponse<>();
        executeService.configData(paramlist, KieSessionType.SECOND);
		...
        return result;
    }

返回结果:

{
“message”: “Student{age=18, name=‘小明-已成年’, message=‘匹配成功:priority-30’}”
}

动态更新规则

    @ApiOperation("动态更新规则")
    public BaseResponse<String> updateKieSession(@RequestParam(value = "type") @ApiParam("规则文件类型") String type,
                                                 @RequestBody MultipartFile[] requestList) throws Exception {

        BaseResponse<String> result = new BaseResponse<>();
        ...
        stringList.add(kieUpdateService.readDrlByStream(stream));
        ... 
        result = kieUpdateService.updateByFile(type, stringList);
        ...
        return result;
    }

复制规则一的drl文件到本地,在文件中增加加一条rule:

rule "myTest three"
	dialect "java"
	no-loop true
	lock-on-active true
    activation-group "myTest"
    salience 40
	when
        std:Student(age > 10)
	then
        std.setMessage(str + ":priority-40");
end

传入本地的drl文件,动态更新session后再次执行规则引擎,返回结果如下:

{
“message”: “Student{age=18, name=‘小明-已成年’, message=‘匹配成功:priority-40’}”
}

动态更新规则过程

  1. 将输入的drl文件转变为字符串
    public String readDrlByStream(InputStream inputStream) {
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                bos.write(buffer, 0, length);
            }
            return bos.toString();
        } catch (IOException e) {
            LOGGER.error("Read file error");
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                LOGGER.error("Close stream error");
            }
        }
        return null;
    }
  1. 使用转为字符串的drl文件,更新指定名称的KieSession
    public boolean updateKieSession(String session, List<String> drlStrList) {
        //1.生成新的KieSession
        KieHelper helper = new KieHelper();
        //添加传入规则
        for (String str : drlStrList) {
            helper.addContent(str, ResourceType.DRL);
        }
        Results results = helper.verify();
        if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
            List<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);
            for (Message message : messages) {
                LOGGER.error("规则语法异常: {}", message.getText());
            }
            throw new IllegalStateException("规则文件语法错误!");
        }
        //生成StatelessKieSession对象,方法一
        //KieBaseConfiguration configuration = helper.ks.newKieBaseConfiguration();
        //configuration.setOption(EventProcessingOption.STREAM);
        //StatelessKieSession kieSession = helper.build(configuration).newStatelessKieSession();
        //方法二
        StatelessKieSession kieSession = helper.build(MultithreadEvaluationOption.YES).newStatelessKieSession();

        //2.校验Kiesession
        KieSessionType kieSessionType = KieSessionType.getVaule(session);
        if (kieSessionType.equals(KieSessionType.OTHER)) {
            LOGGER.error("Session名称错误: " + session);
            return false;
        }

        //3.更新Kiesession
        kieContainerService.updateKieSession(kieSession, kieSessionType);

        return true;
    }

项目地址

https://gitee.com/enchanted-ys/drools-demo
### Drools 中实现规则动态更新Drools中,为了实现实时调整业务逻辑而不重启应用程序,提供了多种机制用于动态更新规则。这不仅提高了系统的灵活性也增强了响应速度。 #### 使用 KieContainer 进行动态更新 当涉及到规则文件的修改时,可以借助`KieContainer`对象完成热部署操作。通过监听资源变化并重新编译受影响的知识包,在不中断服务的情况下使新版本生效[^1]。 ```java // 获取当前容器状态快照以便后续对比差异 ReleaseId releaseId = kieContainer.getReleaseId(); InternalKieModule kieModule = (InternalKieModule) ((KieRepository)kieServices.getRepository()).getKieModule(releaseId); Set<String> ruleNamesBeforeChange = new HashSet<>(Arrays.asList(kieModule.getRuleNames())); // 执行具体更改动作(新增/删除/编辑) File rulesDir = new File("/path/to/rules"); ResourceFactory.newDirectoryStream(rulesDir.toURI(), "*.drl").forEach(resource -> { try { Resource updatedRule = ResourceFactory.newReaderResource(new InputStreamReader(resource.getInputStream())); kieServices.newKieBuilder(updatedRule).buildAll(); // 编译单个或批量规则文件 } catch (IOException e) { /* handle exception */ } }); // 刷新容器以应用最新改动 KieScanner kieScanner = kieServices.newKieScanner(kieContainer); kieScanner.scanNow(); // 验证变更是否成功实施 Set<String> ruleNamesAfterChange = Arrays.stream(((InternalKieModule)kieServices.getRepository().getKieModule(releaseId)).getRuleNames()) .collect(Collectors.toSet()); if (!ruleNamesBeforeChange.equals(ruleNamesAfterChange)) System.out.println("Rules have been dynamically updated."); ``` 上述代码片段展示了如何检测到规则集的变化,并通知 `KieScanner` 来触发即时扫描过程从而同步最新的规则定义至运行环境中[^4]。 对于更复杂的场景比如按需加载特定条件下的规则子集,则可以通过编程方式控制哪些规则应该被激活或者禁用: ```java StatelessKieSession session = kieContainer.newStatelessKieSession(); session.setGlobal("someParameter", value); List<Command<?>> commands = new ArrayList<>(); commands.add(CommandFactory.newInsert(fact)); commands.add(CommandFactory.newFireAllRules()); BatchExecutionCommand batchCmd = CommandFactory.newBatchExecution(commands.toArray(new Command[0])); ResultHandler resultHandler = new ResultHandler() {}; session.execute(batchCmd, resultHandler ); // 可选地设置过滤器仅允许某些命名空间内的规则参与评估 AgendaFilter agendaFilter = new AgendaFilter() { @Override public boolean accept(Match match) { String packageName = match.getRule().getPackageName(); return "specific.package.name".equals(packageName); } }; session.fireUntilHalt(agendaFilter); ``` 这段脚本说明了怎样利用命令模式封装多个操作步骤并通过全局变量传递参数给规则引擎;同时也介绍了基于自定义议程筛选器来限定有效作用范围的技术方案[^3]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值