前言
规则引擎是由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。
本文描述的是一个简单的SpringBoot + Drools项目用例。用例中使用的是Drools规则引擎的无状态会话(StatelessKIESession)。为方便排版部分代码已经过删减,完整代码可到“项目地址”获取。
项目提供两个API接口:
- 执行规则引擎
- 动态更新规则
引入依赖
<!-- 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’}”
}
动态更新规则过程
- 将输入的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;
}
- 使用转为字符串的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