半小时搞定,规则引擎Drools 集成 springboot 热加载

前言:

  如果对drools还不是特别熟悉的,可以看下 《规则引擎Drools 之 初识drools》这篇文章;

  本文源码,github 传送门:https://github.com/vincent9309/drools

  系统架构如下:

 

一、项目搭建流程

在这里插入图片描述

 

二、项目目录结构

三、springboot集成drools 

1.pom文件引入依赖

   <dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>7.0.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-compiler</artifactId>
			<version>7.0.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-decisiontables</artifactId>
			<version>7.0.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-templates</artifactId>
			<version>7.0.0.Final</version>
		</dependency>
 
		<dependency>
			<groupId>org.kie</groupId>
			<artifactId>kie-api</artifactId>
			<version>7.0.0.Final</version>
		</dependency>

2. 在src/main/resources/ruls 新建规则文件:

package plausibcheck.adress

import com.neo.drools.model.Address;
import com.neo.drools.model.fact.AddressCheckResult;

rule "[address] Postcode should be filled with exactly 5 numbers"
    agenda-group "address" // rule 分组
//    activation-group "address" //group里面只能执行一个rule
//    no-loop true // 避免循环调用
//    lock-on-active true  //ruleflow-group属性或agenda-group属性的时候, 避免循环调用
//    salience 3 //权重
//    dialect "java" //方言
//    date-effective "20-Jul-2018" //生效日期
//    date-expires "21-Jul-2019" //失效日期
//    enabled true //是否可用
    when
        address : Address(postcode != null, postcode matches "([0-9]{5})")
        checkResult : AddressCheckResult();
    then
        checkResult.setPostCodeResult(true);
		System.out.println("[address]规则中打印日志:校验通过!");
end

rule "[copy-address] --> Postcode should be filled with exactly 5 numbers"
    agenda-group "address"
    activation-group "address"
    no-loop true
    lock-on-active true
    salience 1
    when
        address : Address(postcode != null, postcode matches "([0-9]{5})")
        checkResult : AddressCheckResult();
    then
        checkResult.setPostCodeResult(true);
		System.out.println("[copy-address]规则中打印日志:校验通过!");
end

3、src/main/resources/META-INF新建配置文件kmodule.xml:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://www.drools.org/xsd/kmodule">

    <kbase name="address-testKB" packages="rules">
        <ksession name="address-testKS"/>
    </kbase>

    <kbase name="addressKB" packages="rules">
        <ksession name="addressKS"/>
    </kbase>

    <kbase name="rules" packages="rules">
        <ksession name="ksession-rule"/>
    </kbase>

</kmodule>

4.创建tKieUitl

public class KieUtils {
    private static KieContainer kieContainer;

    private static KieSession kieSession;

    public static KieContainer getKieContainer() {
        return kieContainer;
    }

    public static void setKieContainer(KieContainer kieContainer) {
        KieUtils.kieContainer = kieContainer;
        kieSession = kieContainer.newKieSession();
    }

    public static KieSession getKieSession() {
        return kieSession;
    }

    public static void setKieSession(KieSession kieSession) {
        KieUtils.kieSession = kieSession;
    }

    public static KieServices getKieServices() {
        return KieServices.Factory.get();
    }
}

5.通过restful API即可调用

@RequestMapping("/address-test")
    public void addressTest(int num){
        Address address = new Address();
        address.setPostcode(generateRandom(num));

        KieServices ks = KieServices.Factory.get();
        KieContainer kc = ks.getKieClasspathContainer();
        KieSession kieSession = kc.newKieSession("address-testKS");

        //选择agenda-group
        kieSession.getAgenda().getAgendaGroup("address-test").setFocus();
        //增加自定义AgendaFilter
        AgendaFilter filter = new MyAgendaFilter("[Address-Test]");

        AddressCheckResult result = new AddressCheckResult();
        FactHandle faceHandle = kieSession.insert(address);
        kieSession.insert(result);

        //更新work memory 中的对象信息
//        address.setPostcode("123");
//        kieSession.update(faceHandle,address);

        int ruleFiredCount = kieSession.fireAllRules(filter);
        kieSession.destroy();
        System.out.println("触发了" + ruleFiredCount + "条规则");

        if(result.isPostCodeResult()){
            System.out.println("规则校验通过");
        }
    }

    /**
     * 生成随机数
     * @param num
     * @return
     */
    public String generateRandom(int num) {
        String chars = "0123456789";
        StringBuffer number=new StringBuffer();
        for (int i = 0; i < num; i++) {
            int rand = (int) (Math.random() * 10);
            number=number.append(chars.charAt(rand));
        }
        return number.toString();
    }

三、springboot热刷新drools 规则文件

 

1.增加config配置

@Configuration
public class DroolsAutoConfig {

    public static final String RULES_PATH = "rules/";
    public static final String BASE_RULES_PATH = "classpath*:";

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources(BASE_RULES_PATH + RULES_PATH + "**/*.*");
    }

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        final KieRepository kieRepository = getKieServices().getRepository();

        kieRepository.addKieModule(new KieModule() {
            public ReleaseId getReleaseId() {
                return kieRepository.getDefaultReleaseId();
            }
        });

        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();

        KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
        KieUtils.setKieContainer(kieContainer);
        return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
    }

    private KieServices getKieServices() {
        return KieServices.Factory.get();
    }

    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }

    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException {
        KieSession kieSession = kieContainer().newKieSession();
        KieUtils.setKieSession(kieSession);
        return kieSession;
    }

    @Bean
    @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

2. 增加两个API接口,一个是更新规则文件(此处就不详细写常规的上传功能);另一个是根据文件刷新规则

   @RequestMapping("/reload")
    public String reload(String drlName) throws Exception {
        rules.reload(drlName);
        return "ok";
    }

    @RequestMapping("/updateDrlFile")
    public String updateDrlFile(MultipartFile file) throws Exception {
        //更新drl后,再调用reload方法重载。即可热部署
        return "ok";
    }

3. 具体看service 实现 (此处也可以读取数据库,刷新规则)

@Service
public class ReloadDroolsRules {

    public void reload(String drlName) throws Exception {
        KieServices kieServices = KieUtils.getKieServices();
        KieFileSystem kfs = kieServices.newKieFileSystem();
        loadDBRules(drlName, kfs);
//        loadFileRules(drlName, kfs);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
        Results results = kieBuilder.getResults();
        if (results.hasMessages(Message.Level.ERROR)) {
            System.out.println(results.getMessages());
            throw new IllegalStateException("### errors ###");
        }

        KieUtils.setKieContainer(kieServices.newKieContainer(KieUtils.getKieServices().getRepository().getDefaultReleaseId()));
        System.out.println("新规则重载成功");
    }

    private void loadDBRules(String drlName, KieFileSystem kfs) {
        //        String path = "src/main/resources/rules/address.drl";
        String path = "src/main/resources/"+ DroolsAutoConfig.RULES_PATH + "/"+ drlName + ".drl";
        // 从数据库加载的规则
        kfs.write(path, "package plausibcheck.adress\n\n import com.neo.drools.model.Address;\n import com.neo.drools.model.fact.AddressCheckResult;\n\n rule \"Postcode 6 numbers\"\n\n    when\n  then\n        System.out.println(\"打印日志:更新rules成功!\");\n end");
    }

    private void loadFileRules(String drlName, KieFileSystem kfs) throws IOException{
        // 从classess/rules加载的规则
        for (Resource file : getRuleFiles(drlName)) {
            kfs.write(ResourceFactory.newClassPathResource(DroolsAutoConfig.BASE_RULES_PATH + DroolsAutoConfig.RULES_PATH + file.getFilename(), "UTF-8"));
        }
    }

    private Resource[] getRuleFiles(String drlName) throws IOException {
        if(StringUtils.isEmpty(drlName)){
            ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            return resourcePatternResolver.getResources(DroolsAutoConfig.BASE_RULES_PATH + DroolsAutoConfig.RULES_PATH + "**/*.*");
        }
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources(DroolsAutoConfig.BASE_RULES_PATH + DroolsAutoConfig.RULES_PATH + "**/"+ drlName + ".*");
    }
}

这样就可以实现规则热加载了,完整代码,可以参考文章头部 github链接, 谢谢!

 

  • 10
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏目 "

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值