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监听(下篇预告)

参考

原文地址:https://www.cnblogs.com/noodlerkun/p/13644648.html