SpringBoot/SpringCloud+nacos下配置使用drools,规则修改后无需重启服务
最近做了个需求,就是http调第三方地图图层接口获取点位信息,要把reponseData转换为我们自己的entity数据结构然后入库,因为每种图层返回的json数据结构都不一样,并且图层种类又很多,于是就考虑使用drools来根据规则动态转换,为了使得规则修改后不需要重启服务,我把.drl配置到了nacos config里,并且加了监听器。
小提示
虽然可以把.drl挂到nacos里,但是一开始最好放到工程的resource目录下,因为这样我们编写.drl规则时idea会给快捷提示。
maven pom引入drools
<drools.version>7.60.0.Final</drools.version> <!-- drools --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${drools.version}</version> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artifactId> <version>${drools.version}</version> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${drools.version}</version> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-compiler</artifactId> <version>${drools.version}</version> </dependency>
drools处理类
import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException;import lombok.extern.slf4j.Slf4j; import org.kie.api.KieBase; import org.kie.api.io.ResourceType; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.QueryResults; import org.kie.api.runtime.rule.QueryResultsRow; import org.kie.internal.utils.KieHelper; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.List; import java.util.Properties; import java.util.concurrent.Executor; @Service @Slf4j public class DroolsLayerHandler { private final static String DEFAULT_GROUP = "DEFAULT_GROUP"; String dataId = "my-parse-http-result-dev.drl"; public ConfigService configService; private KieSession kieSession; @Value("${spring.profiles.address}") private String SERVER_ADDRESS; @PostConstruct public void init() { initConfigService(); registerNacosListener(); loadDroolSession(); } public synchronized void loadDroolSession() { log.info("------------准备载入图层规则"); try { String droolRule = configService.getConfig(dataId, DEFAULT_GROUP, 3000); log.info("获取nacos规则配置信息.\n{}", droolRule); KieHelper kieHelper = new KieHelper(); kieHelper.addContent(droolRule, ResourceType.DRL); KieBase kieBase = kieHelper.build(); if (kieSession != null) { kieSession.dispose(); } kieSession = kieBase.newKieSession(); } catch (Exception e) { log.error("load drools session error", e); } } /** * 触发规则逻辑 * * @param */ public void fireRules(String jsonStr, String layerName, String typeName, String typeCode, LayerDataHandler layerDataHandler) {
// 给drool设置全局变量 kieSession.setGlobal("layerDataHandler", layerDataHandler);
// 给drool设置入参 kieSession.insert(jsonStr); // kieSession.insert(layerName); // kieSession.insert(typeName); // kieSession.insert(typeCode); kieSession.fireAllRules(); } /** * 查询规则引擎里设置的结果数据 */ public List<MyPoint> getQueryResults() { QueryResults results = kieSession.getQueryResults("getMyPointResult"); for (QueryResultsRow row : results) { MyPointResult result = (MyPointResult) row.get("rs"); return result.getPoints(); } return null; }
// 获取nacos config service public void initConfigService() { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.NAMESPACE, ""); properties.put(PropertyKeyConst.SERVER_ADDR, SERVER_ADDRESS); try { configService = NacosFactory.createConfigService(properties); } catch (NacosException e) { log.error("Nacos ConfigService create error", e); } }
// 实时监听配置文件是否改动 private void registerNacosListener() { try { Listener configListener = new Listener() { @Override public void receiveConfigInfo(String configInfo) { log.info("监听到nacos配置的规则{}有改动", dataId);
// 重载规则 loadDroolSession(); } @Override public Executor getExecutor() { return null; // Use the default executor } }; configService.addListener(dataId, DEFAULT_GROUP, configListener); } catch (Exception e) { log.error("Error registering Nacos listener", e); } } }
nacos里配置.drl
规则文件内容
import com.ttttt.entity.MyPoint; import java.util.List import com.tttttt.handler.LayerDataHandler import com.ttttt.dal.dto.drools.MyPointResult; // 声明全局变量 global LayerDataHandler layerDataHandler; // 规则定义 rule "Parse JSON String" when
// 定义的规则,入参是否同时包含三个字符串 $jsonStr : String( this contains "features", this contains "roseName", this contains "geometry" ) then
// 调用Java代码里的具体转换方法 List<MyPoint> parsedPoints = layerDataHandler.parseText($jsonStr); System.out.println("规则被触发了,解析的点数是: " + parsedPoints.size()); MyPointResult rs = new MyPointResult(parsedPoints);
// 设置结果进工作内存 insert(rs); end // 查询结果 query "getMyPointResult" rs : MyPointResult() end
json转换处理类
import cn.hutool.http.HttpUtil; import com.tttttt.dal.entity.MyPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * @Author: 夏威夷8080 * @Date: 2023/8/23 15:50 */ @Component public class LayerMyDataHandler implements LayerDataHandler { @Autowired private DroolsLayerHandler droolsLayerHandler; @Override public String getLayerName() { return "我的图层"; } @Override public String getLayerCode() { return SECTION; } @Override public String getReqDataUrl() { return "http://test.com/pp/tt" + "?type=" + getTypeCode() + "&typeName=" + getTypeName(); } @Override public String getTypeCode() { return "MY_TCLD"; } @Override public String getTypeName() { return "my_tcld"; } @Override public String syncData() { return HttpUtil.createGet(getReqDataUrl()) .execute().body(); } @Override public List<MyPoint> parseText(String httpResult) { List<MyPoint> points = new ArrayList<>(); MyPoint supportLayerPoint = new MyPoint(); supportLayerPoint.setLayerName(getLayerName()); supportLayerPoint.setPointName("my point name"); points.add(supportLayerPoint); return points; } @Override public List<MyPoint> convertByRule(String httpResult) { droolsLayerHandler.fireRules(httpResult, getLayerName(), getTypeName(), getTypeCode(), this); List<MyPoint> queryResults = droolsLayerHandler.getQueryResults(); return queryResults; } }
测试方法
public void test() { LayerDataHandler handler = layerSyncHandlerFactory.getHandler(SECTION); String httpResult = handler.syncData(); List<MyPoint> points = handler.convertByRule(httpResult); System.out.println(points); }