zoukankan      html  css  js  c++  java
  • Drools-决策表使用2-集成springboot

    之前快速使用了决策表,本次接着集成springboot。

    论大象装冰箱需要几步

    • 建springboot-maven工程、添加pom、配置application.yml,添加boot启动类。
    • 将上篇demo的核心类,IOC到springboot中
    • 添加测试controller,运行时可以通过REST接口触发规则刷新动作

    新建springboot工程

    • pom
      <drools.version>7.42.0.Final</drools.version>
      <springboot.version>2.2.5.RELEASE</springboot.version>
      
      <dependencies>
      	<dependency>
      		<groupId>org.drools</groupId>
      		<artifactId>drools-decisiontables</artifactId>
      	</dependency>
      
      	<dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-thymeleaf</artifactId>
      	</dependency>
      	<dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-web</artifactId>
      	</dependency>
      	<dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-actuator</artifactId>
      	</dependency>
      <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> 
      	<scope>runtime</scope> <optional>true</optional> </dependency> -->
      	<dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-configuration-processor</artifactId>
      		<optional>true</optional>
      	</dependency>
      </dependencies>
      
    • application.yml
      server:
        port: 8000
        
      spring:
        application:
          name: @project.artifactId@ 
        profiles:
          active: local
          
        thymeleaf:
          cache: false
      
      app:
        drools:
          # 配置Drools决策表文件路径,可多个
          xlsFilePaths:
          - D:/temp/person_check.xls
      
    • 启动类
      @SpringBootApplication
      public class BootDrools {
      	public static void main(String[] args) {
      		System.setProperty("drools.dateformat","yyyy-MM-dd");
      		SpringApplication.run(BootDrools.class, args);
      	}
      }
      

    核心集成点

    Kie相关类的IOC化 -> KieUtilService

    • KieHelper 私有单例化,只有首次启动和规则重刷时才发生变更
    • 提供规则触发方法
    • 提供规则重刷方法
    • application.yml 配置的文件路径注入
    • 代码:
      @Service
      @ConfigurationProperties(prefix = "app.drools")
      @Slf4j
      public class KieUtilService {
      //	@Autowired
      //	private DroolsRuleService droolsRuleService;
      	
      	private List<String> xlsFilePaths = new ArrayList<>();
      	
      	private KieHelper kieHelper;
      	
      	private final SpreadsheetCompiler compiler = new SpreadsheetCompiler();
      	
      	@PostConstruct
      	public void init() {
      		reload();
      	}
      

    注意KieSession

    • 注意KieHeplerKieContainerKieBaseKieSession的关系
    • 本次做法没有考虑是否有状态Session,直接使用方法/线程级Session,一个触发动作就是一个新的Session
    • 代码:
      /**
       * 触发规则
       * @param fact
       */
      public void fire(Object fact) {
      	this.fire(fact, new FireOption().setFireLimit(-1));
      }
      /**
       * 触发规则,指定触发配置
       * @param fact
       * @param fireLimit
       */
      public void fire(Object fact, FireOption option) {
      	log.info(">> fire, fact:{}, option:{}", fact, option);
      	
      	KieSession session = kieHelper.build().newKieSession();
      	//Drools全局变量
      	if(CollUtil.isNotEmpty(option.getGlobalVariables())) {
      		for(Entry<String, Object> entry: option.getGlobalVariables().entrySet()) {
      			session.setGlobal(entry.getKey(), entry.getValue());
      		}
      	}
      	//fact放入工作内容
      	session.insert(fact);
      	//指定聚焦议程组
      	if(StrUtil.isNotBlank(option.getAgendaGroup())) {
      		session.getAgenda().getAgendaGroup(option.getAgendaGroup()).setFocus();
      	}
      	//触发数量配置
      	int fireLimit = option.getFireLimit()==null ? -1 : option.getFireLimit();
      	int count = session.fireAllRules(fireLimit);
      	
      	log.info("本次触发的规则数:{}", count);
      	session.dispose(); //方法级KieSession
      	
      	log.info("<< fire, fact:{}", fact);
      }
      

    规则重载

    • 重新生成 KieHepler实例
    • 加载指定路径的决策表文件
    • 重载方法:
      /**
       * 重新加载外置的决策表EXCEL规则文件
       */
      public void reload() {
      	log.info(">> reload");
      	log.info("配置的xls规则文件列表:{}", xlsFilePaths.toString());
      
      	// 载入规则
      	this.kieHelper = null;
      	KieHelper newKieHelper = new KieHelper();
      	try {
      		for (String path : xlsFilePaths) {
      			InputStream is = new FileInputStream(new File(path));
      			String drl = compiler.compile(is, InputType.XLS);
      			log.info("{} to compile drl:
      {}", path, drl);
      			newKieHelper.addContent(drl, ResourceType.DRL);
      		}
      		// 测试任意drl来源,测试通过
      		// List<DroolsRule> listAll = droolsRuleService.listAll();
      		// listAll.forEach(o -> newKieHelper.addContent(o.getDrlContent(), ResourceType.DRL));
      	} catch (FileNotFoundException e) {
      		log.error("文件读取异常", e);
      		throw new RuntimeException("文件读取异常", e);
      	}
      
      	// drl文本语法校验
      	verifyDrl(newKieHelper);
      
      	this.kieHelper = newKieHelper;
      	log.info("<< reload");
      }
      
    • 测试controller:
      @RestController
      public class TestCtrller {
      	@Value("${spring.application.name}")
      	private String app_name;
      
      	@Autowired
      	private KieUtilService kieUtilService;
      
          //	@Autowired
          //	private AppDroolsYmlConfigBean ymlBean;
      
      	@GetMapping("/hello")
      	public Object hello() {
      		return app_name + "@" + LocalDateTime.now().toString();
      	}
      
      	@GetMapping("/rule/fire")
      	public Object ruleFire(PersonInfoEntity entity) {
      		kieUtilService.fire(entity);
      		// kieUtilService.fire(entity, new FireOption().setAgendaGroup("sign"));
      		return entity;
      	}
      
      	@GetMapping("/rule/reload")
      	public Object ruleReload() {
      		kieUtilService.reload();
      		return "OK";
      	}
      }
      

    小结

    • 可以看出,本次demo并没有采用网上主流的kie-spring集成方案,而是使用最少依赖结合IOC的方式。
      PS:主流的Drools集成Spring、Spring-Web以及SpringBoot的demo都可以在下方demo源码同级目录中找到。
    • 单节点的集成相对还是简单的,性能上咱也做不了太多改善了。但是多节点集群下,比如Feign的负载均衡,只会重载某个节点,这是个问题。
    • 集群下重载规则解决方案
      • MQ广播
      • Nacos监听(下篇预告)

    参考

  • 相关阅读:
    HYSBZ 1500 [NOI2005]维修数列 splay
    The 15th Zhejiang University Programming Contest
    工作小助手-v1.0正式上线,欢迎体验!!!
    登录窗体登录失败但是MainForm依然弹出无法结束的解决方法
    报错'cannot change visible in onshow or onhide'
    release模式发布软件的方法
    发布软件时因为窗体自动加载次序不对导致报错00000000
    修改类别 (类实现)两种方法
    从记事本导入记录
    快速粘贴
  • 原文地址:https://www.cnblogs.com/noodlerkun/p/13644648.html
Copyright © 2011-2022 走看看