一、背景介绍
上一篇文章是对规则引擎的基本介绍,本篇文章是对于drools规则引擎的基本使用。
二、 思路方案
前提:首先保证主机联网、有docker环境、保证Linux空闲内存在4G左右
三、过程
一、搭建WorkBench
- 拉取镜像到本地
docker pull jboss/drools-workbench-showcase
- 启动镜像
docker run -p 8080:8080 -p 8001:8001 -d --name drools-workbench jboss/drools-workbench-showcase:latest
- 访问WorkBench
浏览器访问http://ip:8080/business-central
4. workbench相关的账号密码:
USER PASSWORD ROLE
*********************************************
admin admin admin,analyst,kiemgmt
krisv krisv admin,analyst
john john analyst,Accounting,PM
sales-rep sales-rep analyst,sales
katy katy analyst,HR
二、搭建Kie-Server
- 拉取镜像到本地
docker pull jboss/kie-server-showcase
- 然后kie-server启动并与workbench关联
docker run -p 8180:8080 -d --name kie-server --link drools-workbench:kie_wb jboss/kie-server-showcase:latest
- 访问kie-server
http://ip:8180/kie-server/services/rest/server/
三、Spring Boot项目整合Drools规则引擎
Spring boot整合Drools之后的最终效果是我在Drools workbench中修改对应的规则。然后将其发布到kie-server上。不用重启的应用程序,该规则可以在程序运行中生效。
示例:
在一个业务场景中我们需要多人员的上次的考核时间进行判断。如果考核时间超过了11个月则该人员需要重新考核。由于对于考核时间可能会根据业务调整发生改变,比如由于需要大家对人员的质量的保证这个时候需要缩短考核时间到5个月。及考核时间超过5个月之后该人员需要重新考核。针对于这样的业务场景我们将考核时间这个规则抽离到drools规则引擎中。
-
在spring boot项目中新建数据实体。
数据实体里面的属性包括人员的基本数据,和上次考核时间以及是否需要重新考核的accessFlag属性。
-
在drools workbench中创建项目并创建实体和配置规则,最后发布服务。
1.进入workbench创建项目
2.创建资产——数据对象
先创建数据对象,必须与项目中的数据对象保持一致(名称和包路径)
项目中的数据对象
数据对象名称和软件包,分别对应项目中的数据对象名称和包路径
软件包可以通过添加资产-软件包补充,最终创建出与项目中数据对象相同的包路径。
3.编写数据对象的代码
将程序中的数据对象的属性和方法复制到源代码中,并保存。
4.编写对应的规则
这里考虑到需要一个可视化界面便于修改规则所以选用了决策表。
决策表的名称和软件包,可以任意修改。
首选引入对应的数据对象
根据自己项目的要求,编写对应的规则。
这里我只是做一个示例:
添加对应的列
先选择Add a Condition 表示添加一个条件
选择计算类型
选择需要参与计算的字段
选择操作符
点击完成之后,可以再Columns中查看
再选择条件通过之后的执行,选择Set the value of a field
再点击Model可以看到可视化界面
选择增加一行
补充上我们的规则数据
点击源代码就可以看到生成的代码,之后点击保存
5.设置kie bases将该项目发布到kie-server中
6.进行构建和发布
7.查看kie-server
访问kie-server容器信息
http://ip:8180/kie-server/services/rest/server/containers
页面中能够找到对应的容器,表示已经发布到kie-server中了。
- 项目引入依赖:
<!-- kie-server -->
<dependency>
<groupId>org.kie.server</groupId>
<artifactId>kie-server-client</artifactId>
<drools.version>7.73.0.Final</drools.version>
</dependency>
<dependency>
<groupId>org.kie.server</groupId>
<artifactId>kie-server-api</artifactId>
<drools.version>7.73.0.Final</drools.version>
</dependency>
- 填写配置文件:
drools服务
kie-server:
url: http://ip:8180/kie-server/services/rest/server
username: ***
password: ***
#配置容器名
containerId: test_1.0.0-SNAPSHOT
timeout: 1000
#在kie-server中配置的session
session: session6
#session: hello-session
- 定义资源类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* kie-server相关配置
*
* @author
* @date 2023/9/6 11:16
**/
@Data
@RefreshScope
@ConfigurationProperties(prefix = "kie-server")
public class KieServerProperties {
/**
* 地址
*/
private String url;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 容器
*/
private String containerId;
/**
* 过期时间
*/
private String timeout;
/**
* 会话名称
*/
private String session;
}
- 封装kie server服务类
import org.kie.api.KieServices;
import org.kie.api.command.Command;
import org.kie.api.command.KieCommands;
import org.kie.api.runtime.ExecutionResults;
import org.kie.server.api.marshalling.MarshallingFormat;
import org.kie.server.api.model.KieServiceResponse;
import org.kie.server.api.model.ServiceResponse;
import org.kie.server.client.KieServicesClient;
import org.kie.server.client.KieServicesConfiguration;
import org.kie.server.client.KieServicesFactory;
import org.kie.server.client.RuleServicesClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 远程kie-server相关服务
*
* @author
* @date 2023/9/6 13:51
**/
@Service
public class KieClientService {
private static final Logger log = LoggerFactory.getLogger(KieClientService.class);
@Autowired
private KieServerProperties kieServerProperties;
/**
* 获取命令对象
*
* @return 命令对象
*/
public KieCommands getKieCommands() {
return KieServices.Factory.get().getCommands();
}
/**
* 获取存放命令的LinkedList
*
* @return 存放命令的LinkedList
*/
public List<Command<?>> getKieCommandList() {
return new LinkedList<>();
}
/**
* 执行规则
*
* @param commandList 存放命令的LinkedList
* @param kieCommands 命令对象
* @return 执行结果
*/
public ExecutionResults executeCommands(List<Command<?>> commandList, KieCommands kieCommands) {
RuleServicesClient client = this.getClient();
ServiceResponse<ExecutionResults> serviceResponse = client.executeCommandsWithResults(kieServerProperties.getContainerId(),
kieCommands.newBatchExecution(commandList, kieServerProperties.getSession()));
if (serviceResponse == null || !KieServiceResponse.ResponseType.SUCCESS.equals(serviceResponse.getType())) {
log.error("调用kie-server失败");
throw ServiceExceptionUtil.exception(new ErrorCode(500, "调用kie-server失败"));
}
return serviceResponse.getResult();
}
/**
* 根据配置获取连接
*
* @return 与kie-server的连接
*/
private RuleServicesClient getClient() {
//配置信息
KieServicesConfiguration kieServicesConfiguration = KieServicesFactory.newRestConfiguration(
kieServerProperties.getUrl(), kieServerProperties.getUsername(),
kieServerProperties.getPassword(), Long.parseLong(kieServerProperties.getTimeout()));
kieServicesConfiguration.setMarshallingFormat(MarshallingFormat.JSON);
RuleServicesClient servicesClient;
try {
//创建规则服务客户端
KieServicesClient kieServicesClient = KieServicesFactory.newKieServicesClient(kieServicesConfiguration);
servicesClient = kieServicesClient.getServicesClient(RuleServicesClient.class);
} catch (Exception exception) {
log.error(exception.getMessage());
throw ServiceExceptionUtil.exception(new ErrorCode(500, "无法建立到kie-server的连接"));
}
if (servicesClient == null) {
log.error("servicesClient为null");
throw ServiceExceptionUtil.exception(new ErrorCode(500, "无法建立到kie-server的连接"));
}
return servicesClient;
}
/**
* 将list插入kie-server工作内存并处理
*
* @param list 要插入的数据列表
* @param <T> 数据的类型
* @return 原列表,内容已改变
*/
public <T> List<T> processList(List<T> list) {
if (StringUtils.isEmpty(list)) {
return new ArrayList<>();
}
List<Command<?>> kieCommandList = this.getKieCommandList();
KieCommands kieCommands = this.getKieCommands();
for (int i = 0; i < list.size(); i++) {
kieCommandList.add(kieCommands.newInsert(list.get(i), String.valueOf(i)));
}
ExecutionResults executionResults = this.executeCommands(kieCommandList, kieCommands);
for (int i = 0; i < list.size(); i++) {
BeanUtils.copyProperties(executionResults.getValue(String.valueOf(i)), list.get(i));
}
return list;
}
}
- 编写测试代码
@SpringBootTest
public class TrainingApplicationTest {
@Autowired
private KieClientService kieClientService;
@Test
public void test(){
QualificationAssessmentDTO qualificationAssessmentDTO=new QualificationAssessmentDTO();
qualificationAssessmentDTO.setScore(2.4);
qualificationAssessmentDTO.setType("飞行签派员");
qualificationAssessmentDTO.setIntervalTime(12);
KieCommands kieCommands = kieClientService.getKieCommands();
List<Command<?>> kieCommandList = kieClientService.getKieCommandList();
kieCommandList.add(kieCommands.newInsert(qualificationAssessmentDTO,"qualificationAssessmentDTO"));
ExecutionResults executionResults = kieClientService.executeCommands(kieCommandList, kieCommands);
QualificationAssessmentDTO dto = (QualificationAssessmentDTO) executionResults.getValue("qualificationAssessmentDTO");
System.out.println(JsonUtils.toJsonString(dto));
}
}
测试结果:
在workbench中修改判断条件的值为5,并重新发布。
可以发现规则已经生效。
四、总结
drools规则引擎运行逻辑:在drools-workBench中编写对应的规则最后将其发布到kie-server中。在我们程序中集成kie-Server将我们需要进行规则判断的model传输到kie-server进行规则判断。kie-server再将修改之后的mode传输到我们的应用程序中。由于规则和规则的判断是放到drools-workbench和kie-server中。所以当规则发生变动,并不会影响到原有的应用程序的正常运行。