enhancement #I4FSHW 优雅刷新配置的支持

This commit is contained in:
bryan31
2021-11-11 00:50:19 +08:00
parent 30b28729e0
commit 8be6d00c82
12 changed files with 263 additions and 61 deletions

View File

@@ -257,8 +257,9 @@ public class FlowExecutor {
return null;
}
//此方法就是从原有的配置源主动拉取新的进行刷新
//和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取一个为被动监听到新的内容进行刷新
public void reloadRule() {
FlowBus.cleanCache();
init();
}

View File

@@ -25,9 +25,12 @@ import com.yomahub.liteflow.exception.NodeTypeNotSupportException;
import com.yomahub.liteflow.parser.LocalJsonFlowParser;
import com.yomahub.liteflow.parser.LocalXmlFlowParser;
import com.yomahub.liteflow.parser.LocalYmlFlowParser;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.script.ScriptExecutor;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import com.yomahub.liteflow.script.exception.ScriptSpiException;
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
import com.yomahub.liteflow.util.SpringAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,9 +47,9 @@ public class FlowBus {
private static final Logger LOG = LoggerFactory.getLogger(FlowBus.class);
private static final Map<String, Chain> chainMap = new HashMap<>();
private static final Map<String, Chain> chainMap = new CopyOnWriteHashMap<>();
private static final Map<String, Node> nodeMap = new HashMap<>();
private static final Map<String, Node> nodeMap = new CopyOnWriteHashMap<>();
private FlowBus() {
}
@@ -75,12 +78,10 @@ public class FlowBus {
}
public static void addSpringScanNode(String nodeId, NodeComponent nodeComponent) {
if (containNode(nodeId)) return;
nodeMap.put(nodeId, new Node(ComponentInitializer.loadInstance().initComponent(nodeComponent, NodeTypeEnum.COMMON, null, nodeId)));
}
public static void addCommonNode(String nodeId, String name, String cmpClazzStr) throws Exception {
if (containNode(nodeId)) return;
Class<NodeComponent> cmpClazz = (Class<NodeComponent>) Class.forName(cmpClazzStr);
addNode(nodeId, name, NodeTypeEnum.COMMON, cmpClazz, null);
}
@@ -90,17 +91,14 @@ public class FlowBus {
}
public static void addCommonScriptNode(String nodeId, String name, String script){
if (containNode(nodeId)) return;
addNode(nodeId, name, NodeTypeEnum.SCRIPT, ScriptComponent.class, script);
}
public static void addCondScriptNode(String nodeId, String name, String script){
if (containNode(nodeId)) return;
addNode(nodeId, name, NodeTypeEnum.COND_SCRIPT, ScriptCondComponent.class, script);
}
private static void addNode(String nodeId, String name, NodeTypeEnum type, Class<? extends NodeComponent> cmpClazz, String script) {
if (containNode(nodeId)) return;
try {
//以node方式配置本质上是为了适配无spring的环境如果有spring环境其实不用这么配置
//这里的逻辑是判断是否能从spring上下文中取到如果没有spring则就是new instance了
@@ -149,6 +147,10 @@ public class FlowBus {
public static void cleanCache() {
chainMap.clear();
nodeMap.clear();
cleanScriptCache();
}
public static void cleanScriptCache() {
//如果引入了脚本组件SPI则还需要清理脚本的缓存
try{
ScriptExecutor scriptExecutor = ScriptExecutorFactory.loadInstance().getScriptExecutor();
@@ -160,7 +162,6 @@ public class FlowBus {
//目前这种方式刷新不完全平滑
public static void refreshFlowMetaData(FlowParserTypeEnum type, String content) throws Exception {
FlowBus.cleanCache();
if (type.equals(FlowParserTypeEnum.TYPE_XML)) {
new LocalXmlFlowParser().parse(content);
} else if (type.equals(FlowParserTypeEnum.TYPE_JSON)) {

View File

@@ -85,24 +85,20 @@ public abstract class JsonFlowParser extends FlowParser {
//这里区分是普通java节点还是脚本节点
//如果是脚本节点,又区分是普通脚本节点,还是条件脚本节点
if (nodeTypeEnum.equals(NodeTypeEnum.COMMON) && StrUtil.isNotBlank(clazz)){
if (!FlowBus.containNode(id)){
FlowBus.addCommonNode(id, name, clazz);
}
FlowBus.addCommonNode(id, name, clazz);
}else if(nodeTypeEnum.equals(NodeTypeEnum.SCRIPT) || nodeTypeEnum.equals(NodeTypeEnum.COND_SCRIPT)){
if (!FlowBus.containNode(id)){
//如果file字段不为空则优先从file里面读取脚本文本
if (StrUtil.isNotBlank(file)){
script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", file));
}else{
script = nodeObject.getString("value");
}
//如果file字段不为空则优先从file里面读取脚本文本
if (StrUtil.isNotBlank(file)){
script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", file));
}else{
script = nodeObject.getString("value");
}
//根据节点类型把脚本添加到元数据里
if (nodeTypeEnum.equals(NodeTypeEnum.SCRIPT)){
FlowBus.addCommonScriptNode(id, name, script);
}else {
FlowBus.addCondScriptNode(id, name, script);
}
//根据节点类型把脚本添加到元数据里
if (nodeTypeEnum.equals(NodeTypeEnum.SCRIPT)){
FlowBus.addCommonScriptNode(id, name, script);
}else {
FlowBus.addCondScriptNode(id, name, script);
}
}
}
@@ -112,10 +108,7 @@ public abstract class JsonFlowParser extends FlowParser {
JSONArray chainArray = flowJsonObject.getJSONObject("flow").getJSONArray("chain");
for (int i = 0; i < chainArray.size(); i++) {
JSONObject jsonObject = chainArray.getJSONObject(i);
String chainName = jsonObject.getString("name");
if (!FlowBus.containChain(chainName)) {
parseOneChain(jsonObject, flowJsonObjectList);
}
parseOneChain(jsonObject, flowJsonObjectList);
}
}
} catch (Exception e) {

View File

@@ -89,24 +89,20 @@ public abstract class XmlFlowParser extends FlowParser {
//这里区分是普通java节点还是脚本节点
//如果是脚本节点,又区分是普通脚本节点,还是条件脚本节点
if (nodeTypeEnum.equals(NodeTypeEnum.COMMON) && StrUtil.isNotBlank(clazz)){
if (!FlowBus.containNode(id)){
FlowBus.addCommonNode(id, name, clazz);
}
FlowBus.addCommonNode(id, name, clazz);
}else if(nodeTypeEnum.equals(NodeTypeEnum.SCRIPT) || nodeTypeEnum.equals(NodeTypeEnum.COND_SCRIPT)){
if (!FlowBus.containNode(id)){
//如果file字段不为空则优先从file里面读取脚本文本
if (StrUtil.isNotBlank(file)){
script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", file));
}else{
script = e.getTextTrim();
}
//如果file字段不为空则优先从file里面读取脚本文本
if (StrUtil.isNotBlank(file)){
script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", file));
}else{
script = e.getTextTrim();
}
//根据节点类型把脚本添加到元数据里
if (nodeTypeEnum.equals(NodeTypeEnum.SCRIPT)){
FlowBus.addCommonScriptNode(id, name, script);
}else {
FlowBus.addCondScriptNode(id, name, script);
}
//根据节点类型把脚本添加到元数据里
if (nodeTypeEnum.equals(NodeTypeEnum.SCRIPT)){
FlowBus.addCommonScriptNode(id, name, script);
}else {
FlowBus.addCondScriptNode(id, name, script);
}
}
}
@@ -115,10 +111,7 @@ public abstract class XmlFlowParser extends FlowParser {
// 解析chain节点
List<Element> chainList = rootElement.elements("chain");
for (Element e : chainList) {
String chainName = e.attributeValue("name");
if (!FlowBus.containChain(chainName)) {
parseOneChain(e, documentList);
}
parseOneChain(e, documentList);
}
}
}

View File

@@ -59,7 +59,6 @@ public class ZookeeperXmlFlowParser extends XmlFlowParser{
cache.getListenable().addListener(() -> {
String content1 = new String(cache.getCurrentData().getData());
LOG.info("stating load flow config....");
FlowBus.cleanCache();
parse(content1);
});
}

View File

@@ -0,0 +1,171 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.yomahub.liteflow.util;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A basic copy on write HashMap.
* <p>
* If an instance is cloned then any methods invoked on the instance or clone
* that result in state modification will result in copying of the state
* before modification.
*
* @author Paul.Sandoz@Oracle.Com
* @author pavel.bucek@oracle.com
*/
public class CopyOnWriteHashMap<K,V> implements Map<K,V> {
private volatile Map<K,V> core;
volatile Map<K,V> view;
private final AtomicBoolean requiresCopyOnWrite;
public CopyOnWriteHashMap() {
this.core = new HashMap<K, V>();
this.requiresCopyOnWrite = new AtomicBoolean(false);
}
private CopyOnWriteHashMap(CopyOnWriteHashMap<K,V> that) {
this.core = that.core;
this.requiresCopyOnWrite = new AtomicBoolean(true);
}
@Override
public CopyOnWriteHashMap<K,V> clone() {
try {
return new CopyOnWriteHashMap(this);
} finally {
requiresCopyOnWrite.set(true);
}
}
private void copy() {
if (requiresCopyOnWrite.compareAndSet(true, false)) {
core = new HashMap<K, V>(core);
view = null;
}
}
@Override
public int size() {
return core.size();
}
@Override
public boolean isEmpty() {
return core.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return core.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return core.containsValue(value);
}
@Override
public V get(Object key) {
return core.get(key);
}
@Override
public V put(K key, V value) {
copy();
return core.put(key, value);
}
@Override
public V remove(Object key) {
copy();
return core.remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
copy();
core.putAll(t);
}
@Override
public void clear() {
core = new HashMap<K, V>();
view = null;
copy();
}
@Override
public Set<K> keySet() {
return getView().keySet();
}
@Override
public Collection<V> values() {
return getView().values();
}
@Override
public Set<Entry<K,V>> entrySet() {
return getView().entrySet();
}
@Override
public String toString() {
return core.toString();
}
private Map<K, V> getView() {
Map<K, V> result = view; // volatile read
if (result == null) {
result = Collections.unmodifiableMap(core);
view = result; // volatile write
}
return result;
}
}