之前快速使用了决策表,本次接着集成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
- 注意
KieHepler
、KieContainer
、KieBase
和KieSession
的关系 - 本次做法没有考虑是否有状态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监听(下篇预告)