1. chain、script分离方式支持Apollo动态配置
This commit is contained in:
parent
82028d33c4
commit
7e2e0c337c
|
@ -26,10 +26,6 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
|||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.core.FlowInitHook;
|
||||
import com.yomahub.liteflow.parser.apollo.exception.ApolloException;
|
||||
import com.yomahub.liteflow.parser.apollo.util.ApolloParseHelper;
|
||||
import com.yomahub.liteflow.parser.apollo.vo.ApolloParserConfigVO;
|
||||
|
@ -13,7 +14,6 @@ import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
|||
import com.yomahub.liteflow.util.JsonUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @Description:
|
||||
|
@ -33,29 +33,32 @@ public class ApolloXmlELParser extends ClassXmlFlowELParser {
|
|||
} else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) {
|
||||
apolloParserConfigVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), ApolloParserConfigVO.class);
|
||||
}
|
||||
|
||||
// check config
|
||||
if (Objects.isNull(apolloParserConfigVO)) {
|
||||
throw new ApolloException("ruleSourceExtData or map is empty");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(apolloParserConfigVO.getChainNamespace())) {
|
||||
throw new ApolloException("chainNamespace is empty, you must configure the chainNamespace property");
|
||||
}
|
||||
|
||||
apolloParseHelper = new ApolloParseHelper(apolloParserConfigVO);
|
||||
} catch (Exception e) {
|
||||
throw new ApolloException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String parseCustom() {
|
||||
Consumer<String> parseConsumer = t -> {
|
||||
try {
|
||||
parse(t);
|
||||
} catch (Exception e) {
|
||||
throw new ApolloException(e.getMessage());
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
String content = apolloParseHelper.getContent();
|
||||
apolloParseHelper.checkContent(content);
|
||||
apolloParseHelper.listen(parseConsumer);
|
||||
FlowInitHook.addHook(() -> {
|
||||
apolloParseHelper.listenApollo();
|
||||
return true;
|
||||
});
|
||||
return content;
|
||||
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
package com.yomahub.liteflow.parser.apollo.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ctrip.framework.apollo.ConfigFile;
|
||||
import com.ctrip.framework.apollo.Config;
|
||||
import com.ctrip.framework.apollo.ConfigService;
|
||||
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
|
||||
import com.yomahub.liteflow.exception.ParseException;
|
||||
import com.ctrip.framework.apollo.enums.PropertyChangeType;
|
||||
import com.ctrip.framework.apollo.model.ConfigChange;
|
||||
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
|
||||
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.parser.apollo.exception.ApolloException;
|
||||
import com.yomahub.liteflow.parser.apollo.vo.ApolloParserConfigVO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Description:
|
||||
|
@ -23,54 +32,197 @@ public class ApolloParseHelper {
|
|||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class);
|
||||
|
||||
private final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
|
||||
|
||||
private final String NODE_XML_PATTERN = "<nodes>{}</nodes>";
|
||||
|
||||
private final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node>";
|
||||
|
||||
private final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>";
|
||||
|
||||
private final ApolloParserConfigVO apolloParserConfigVO;
|
||||
|
||||
private ConfigFile liteflowConfigFile;
|
||||
private Config chainConfig;
|
||||
|
||||
private Config scriptConfig;
|
||||
|
||||
|
||||
public ApolloParseHelper(ApolloParserConfigVO apolloParserConfigVO) {
|
||||
this.apolloParserConfigVO = apolloParserConfigVO;
|
||||
try {
|
||||
liteflowConfigFile = ConfigService.getConfigFile(apolloParserConfigVO.getNamespace(), ConfigFileFormat.XML);
|
||||
chainConfig = ConfigService.getConfig(apolloParserConfigVO.getChainNamespace());
|
||||
String scriptNamespace;
|
||||
// scriptConfig is optional
|
||||
if (StrUtil.isNotBlank(scriptNamespace = apolloParserConfigVO.getScriptNamespace())) {
|
||||
scriptConfig = ConfigService.getConfig(scriptNamespace);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ApolloParseHelper] liteflowConfigFile get init error, apolloParserConfigVO:{}", apolloParserConfigVO);
|
||||
throw new ApolloException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
|
||||
try {
|
||||
ConfigFile configFile = ConfigService.getConfigFile(apolloParserConfigVO.getNamespace(), ConfigFileFormat.XML);
|
||||
String content;
|
||||
if (Objects.isNull(configFile) || StrUtil.isBlank(content = configFile.getContent())) {
|
||||
throw new ApolloException(String.format("not find config file, namespace:%s", apolloParserConfigVO.getNamespace()));
|
||||
// 1. handle chain
|
||||
Set<String> propertyNames = chainConfig.getPropertyNames();
|
||||
if (CollectionUtil.isEmpty(propertyNames)) {
|
||||
throw new ApolloException(StrUtil.format("There are no chains in namespace : {}", apolloParserConfigVO.getChainNamespace()));
|
||||
}
|
||||
return content;
|
||||
List<String> chainItemContentList = propertyNames.stream()
|
||||
.map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY)))
|
||||
.collect(Collectors.toList());
|
||||
// merge all chain content
|
||||
String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY);
|
||||
|
||||
// 2. handle script if needed
|
||||
String scriptAllContent = StrUtil.EMPTY;
|
||||
Set<String> scriptNamespaces;
|
||||
if (Objects.nonNull(scriptConfig) && CollectionUtil.isNotEmpty(scriptNamespaces = scriptConfig.getPropertyNames())) {
|
||||
|
||||
List<String> scriptItemContentList = scriptNamespaces.stream()
|
||||
.map(item -> convert(item, scriptConfig.getProperty(item, StrUtil.EMPTY)))
|
||||
.filter(Objects::nonNull)
|
||||
.map(item -> StrUtil.format(NODE_ITEM_XML_PATTERN, item.getNodeId(), item.getName(), item.getType(), item.getScript()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
scriptAllContent = StrUtil.format(NODE_XML_PATTERN, CollUtil.join(scriptItemContentList, StrUtil.EMPTY));
|
||||
}
|
||||
|
||||
return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent);
|
||||
} catch (Exception e) {
|
||||
throw new ApolloException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 content 是否合法
|
||||
*/
|
||||
public void checkContent(String content) {
|
||||
if (StrUtil.isBlank(content)) {
|
||||
String error = StrUtil.format("the node[{}] value is empty", apolloParserConfigVO.toString());
|
||||
throw new ParseException(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听 apollo 数据变化
|
||||
* listen apollo config change
|
||||
*/
|
||||
public void listen(Consumer<String> parseConsumer) {
|
||||
try {
|
||||
liteflowConfigFile.addChangeListener(configFileChangeEvent -> {
|
||||
String newContext = configFileChangeEvent.getNewValue();
|
||||
parseConsumer.accept(newContext);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new ApolloException(e.getMessage());
|
||||
public void listenApollo() {
|
||||
|
||||
// chain
|
||||
chainConfig.addChangeListener(changeEvent ->
|
||||
changeEvent.changedKeys().forEach(changeKey -> {
|
||||
ConfigChange configChange = changeEvent.getChange(changeKey);
|
||||
String newValue = configChange.getNewValue();
|
||||
PropertyChangeType changeType = configChange.getChangeType();
|
||||
switch (changeType) {
|
||||
case ADDED:
|
||||
case MODIFIED:
|
||||
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, newValue);
|
||||
LiteFlowChainELBuilder.createChain()
|
||||
.setChainId(changeKey)
|
||||
.setEL(newValue)
|
||||
.build();
|
||||
break;
|
||||
case DELETED:
|
||||
LOG.info("starting reload flow config... delete key={}", changeKey);
|
||||
FlowBus.removeChain(changeKey);
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
// script
|
||||
if (Objects.isNull(scriptConfig)) {
|
||||
// no script config
|
||||
return;
|
||||
}
|
||||
scriptConfig.addChangeListener(changeEvent ->
|
||||
changeEvent.changedKeys().forEach(changeKey -> {
|
||||
ConfigChange configChange = changeEvent.getChange(changeKey);
|
||||
String newValue = configChange.getNewValue();
|
||||
|
||||
PropertyChangeType changeType = configChange.getChangeType();
|
||||
|
||||
NodeSimpleVO nodeSimpleVO;
|
||||
switch (changeType) {
|
||||
case ADDED:
|
||||
case MODIFIED:
|
||||
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, newValue);
|
||||
nodeSimpleVO = convert(changeKey, newValue);
|
||||
LiteFlowNodeBuilder.createScriptNode()
|
||||
.setId(nodeSimpleVO.getNodeId())
|
||||
.setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType()))
|
||||
.setName(nodeSimpleVO.getName())
|
||||
.setScript(nodeSimpleVO.getScript())
|
||||
.build();
|
||||
break;
|
||||
case DELETED:
|
||||
LOG.info("starting reload flow config... delete key={}", changeKey);
|
||||
nodeSimpleVO = convert(changeKey, null);
|
||||
FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId());
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private NodeSimpleVO convert(String key, String value) {
|
||||
//不需要去理解这串正则,就是一个匹配冒号的
|
||||
//一定得是a:b,或是a:b:c...这种完整类型的字符串的
|
||||
List<String> matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", key);
|
||||
if (CollUtil.isEmpty(matchItemList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NodeSimpleVO nodeSimpleVO = new NodeSimpleVO();
|
||||
if (matchItemList.size() > 1) {
|
||||
nodeSimpleVO.setNodeId(matchItemList.get(0));
|
||||
nodeSimpleVO.setType(matchItemList.get(1));
|
||||
}
|
||||
|
||||
if (matchItemList.size() > 2) {
|
||||
nodeSimpleVO.setName(matchItemList.get(2));
|
||||
}
|
||||
|
||||
// set script
|
||||
nodeSimpleVO.setScript(value);
|
||||
|
||||
return nodeSimpleVO;
|
||||
}
|
||||
|
||||
|
||||
private static class NodeSimpleVO {
|
||||
|
||||
private String nodeId;
|
||||
|
||||
private String type;
|
||||
|
||||
private String name = StrUtil.EMPTY;
|
||||
|
||||
private String script;
|
||||
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public void setScript(String script) {
|
||||
this.script = script;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,28 +7,40 @@ package com.yomahub.liteflow.parser.apollo.vo;
|
|||
*/
|
||||
public class ApolloParserConfigVO {
|
||||
|
||||
private String namespace;
|
||||
private String chainNamespace;
|
||||
|
||||
private String scriptNamespace;
|
||||
|
||||
|
||||
public ApolloParserConfigVO() {
|
||||
}
|
||||
|
||||
public ApolloParserConfigVO(String namespace) {
|
||||
this.namespace = namespace;
|
||||
public ApolloParserConfigVO(String chainNamespace, String scriptNamespace) {
|
||||
this.chainNamespace = chainNamespace;
|
||||
this.scriptNamespace = scriptNamespace;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
public String getChainNamespace() {
|
||||
return chainNamespace;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
public void setChainNamespace(String chainNamespace) {
|
||||
this.chainNamespace = chainNamespace;
|
||||
}
|
||||
|
||||
public String getScriptNamespace() {
|
||||
return scriptNamespace;
|
||||
}
|
||||
|
||||
public void setScriptNamespace(String scriptNamespace) {
|
||||
this.scriptNamespace = scriptNamespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApolloParserConfigVO{" +
|
||||
"namespace='" + namespace + '\'' +
|
||||
"chainNamespace='" + chainNamespace + '\'' +
|
||||
", scriptNamespace='" + scriptNamespace + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue