From 23f5cfb8660790567dd47d8e698c365901328893 Mon Sep 17 00:00:00 2001 From: bryan31 Date: Wed, 22 Sep 2021 20:09:48 +0800 Subject: [PATCH] =?UTF-8?q?feature=20enhancement=20#I49FDK=20=E4=B8=AD?= =?UTF-8?q?=E6=96=AD=E9=87=8D=E8=AF=95=E7=9B=AE=E5=89=8D=E6=98=AF=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=9A=84=EF=BC=8C=E5=B8=8C=E6=9C=9B=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=92=88=E5=AF=B9=E4=B8=AA=E5=88=AB=E7=BB=84=E4=BB=B6=E5=92=8C?= =?UTF-8?q?=E7=89=B9=E5=AE=9Aexception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/annotation/RetryCount.java | 26 ++++++ .../liteflow/core/ComponentInitializer.java | 59 +++++++++++++ .../yomahub/liteflow/core/NodeComponent.java | 22 +++++ .../yomahub/liteflow/entity/data/AbsSlot.java | 4 +- .../yomahub/liteflow/entity/flow/Chain.java | 41 +++++---- .../com/yomahub/liteflow/flow/FlowBus.java | 16 ++-- .../liteflow/parser/JsonFlowParser.java | 2 +- .../liteflow/parser/XmlFlowParser.java | 2 +- .../liteflow/spring/ComponentScanner.java | 84 +++++++++---------- .../script/groovy/GroovyScriptExecutor.java | 2 +- .../test/flowmeta/FlowMetaSpringbootTest.java | 4 +- 11 files changed, 187 insertions(+), 75 deletions(-) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/annotation/RetryCount.java create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/core/ComponentInitializer.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/RetryCount.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/RetryCount.java new file mode 100644 index 000000000..e6c86e502 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/RetryCount.java @@ -0,0 +1,26 @@ +package com.yomahub.liteflow.annotation; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +/** + * LiteFlow组件重试次数 + * + * @author Bryan.Zhang + * @since 2.6.0 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RetryCount { + + @AliasFor(value = "retry") + int value() default 0; + + @AliasFor(value = "value") + int retry() default 0; + + Class[] forExceptions() default {Exception.class}; +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/ComponentInitializer.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/ComponentInitializer.java new file mode 100644 index 000000000..7baaebe56 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/ComponentInitializer.java @@ -0,0 +1,59 @@ +package com.yomahub.liteflow.core; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.RetryCount; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; + +/** + * 组件初始化器 + * @author Bryan.Zhang + * @since 2.6.0 + */ +public class ComponentInitializer { + + private static ComponentInitializer instance; + + public static ComponentInitializer loadInstance(){ + if (ObjectUtil.isNull(instance)){ + instance = new ComponentInitializer(); + } + return instance; + } + + public NodeComponent initComponent(NodeComponent nodeComponent, NodeTypeEnum type, String desc, String nodeId){ + nodeComponent.setNodeId(nodeId); + nodeComponent.setSelf(nodeComponent); + nodeComponent.setType(type); + + //先取传进来的name值(配置文件中配置的),再看有没有配置LiteflowComponent标注 + nodeComponent.setName(desc); + if (nodeComponent.getType().equals(NodeTypeEnum.COMMON) && StrUtil.isBlank(nodeComponent.getName())){ + //判断NodeComponent是否是标识了@LiteflowComponent的标注 + //如果标注了,那么要从中取到name字段 + LiteflowComponent liteflowComponent = nodeComponent.getClass().getAnnotation(LiteflowComponent.class); + if (ObjectUtil.isNotNull(liteflowComponent)) { + String name = liteflowComponent.name(); + if (StrUtil.isNotBlank(name)) { + nodeComponent.setName(name); + } + } + } + + //先从组件上取@RetryCount标注,如果没有,则看全局配置,全局配置如果不配置的话,默认是0 + //默认retryForExceptions为Exception.class + RetryCount retryCountAnnotation = nodeComponent.getClass().getAnnotation(RetryCount.class); + if (ObjectUtil.isNotNull(retryCountAnnotation)) { + nodeComponent.setRetryCount(retryCountAnnotation.retry()); + nodeComponent.setRetryForExceptions(retryCountAnnotation.forExceptions()); + } else { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + nodeComponent.setRetryCount(liteflowConfig.getRetryCount()); + } + + return nodeComponent; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index ce2e02738..94e417435 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -50,6 +50,12 @@ public abstract class NodeComponent { //为何要设置这个,用this不行么,因为如果有aop去切的话,this在spring的aop里是切不到的。self对象有可能是代理过的对象 private NodeComponent self; + //重试次数 + private int retryCount = 0; + + //在目标异常抛出时才重试 + private Class[] retryForExceptions = new Class[]{Exception.class}; + //是否结束整个流程,这个只对串行流程有效,并行流程无效 private final TransmittableThreadLocal isEndTL = new TransmittableThreadLocal<>(); @@ -191,4 +197,20 @@ public abstract class NodeComponent { public T getPrivateDeliveryData(){ return this.getSlot().getPrivateDeliveryData(this.getNodeId()); } + + public int getRetryCount() { + return retryCount; + } + + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + + public Class[] getRetryForExceptions() { + return retryForExceptions; + } + + public void setRetryForExceptions(Class[] retryForExceptions) { + this.retryForExceptions = retryForExceptions; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java index debde72c1..f6a8b55e2 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java @@ -10,6 +10,8 @@ package com.yomahub.liteflow.entity.data; import cn.hutool.core.util.StrUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sun.lwawt.macosx.CSystemTray; + import java.util.Deque; import java.util.Iterator; import java.util.Queue; @@ -99,7 +101,7 @@ public abstract class AbsSlot implements Slot { public void setPrivateDeliveryData(String nodeId, T t){ String privateDKey = PRIVATE_DELIVERY_PREFIX + nodeId; - synchronized (nodeId){ + synchronized (this){ if (dataMap.containsKey(privateDKey)){ Queue queue = (Queue) dataMap.get(privateDKey); queue.add(t); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java index 52596c365..48a818460 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java @@ -26,11 +26,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.*; +import java.util.function.Consumer; +import java.util.function.Predicate; /** * chain对象,实现可执行器 + * * @author Bryan.Zhang */ public class Chain implements Executable { @@ -69,8 +73,6 @@ public class Chain implements Executable { throw new FlowSystemException("no conditionList in this chain[" + chainName + "]"); } - LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - Slot slot = DataBus.getSlot(slotIndex); //循环chain里包含的condition,每一个condition有可能是then,也有可能是when @@ -78,20 +80,29 @@ public class Chain implements Executable { for (Condition condition : conditionList) { if (condition instanceof ThenCondition) { for (Executable executableItem : condition.getNodeList()) { - //进行重试循环判断,如果重试次数为0,则只进行一次循环 - for (int i = 0; i <= liteflowConfig.getRetryCount(); i++) { - try { - if (i > 0) { - LOG.info("[{}]:component[{}] performs {} retry", slot.getRequestId(), executableItem.getExecuteName(), i + 1); - } - executableItem.execute(slotIndex); - break; - } catch (ChainEndException e){ - //如果是ChainEndException,则无需重试 - throw e; - } catch (Exception e) { - if (i >= liteflowConfig.getRetryCount()){ + if (executableItem.getExecuteType().equals(ExecuteTypeEnum.CHAIN)) { + executableItem.execute(slotIndex); + } else { + int retryCount = ((Node)executableItem).getInstance().getRetryCount(); + List> forExceptions = Arrays.asList(((Node)executableItem).getInstance().getRetryForExceptions()); + for (int i = 0; i <= retryCount; i++) { + try { + if (i > 0) { + LOG.info("[{}]:component[{}] performs {} retry", slot.getRequestId(), executableItem.getExecuteName(), i + 1); + } + executableItem.execute(slotIndex); + break; + } catch (ChainEndException e) { + //如果是ChainEndException,则无需重试 throw e; + } catch (Exception e) { + //判断抛出的异常是不是指定异常的子类 + boolean flag = forExceptions.stream().anyMatch(clazz -> clazz.isAssignableFrom(e.getClass())); + + //两种情况不重试,1)抛出异常不在指定异常范围内 2)已经重试次数大于等于配置次数 + if (!flag || i >= retryCount){ + throw e; + } } } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java index 062a41e73..7f7fbc8e9 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java @@ -11,6 +11,7 @@ package com.yomahub.liteflow.flow; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.core.ComponentInitializer; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.core.ScriptComponent; import com.yomahub.liteflow.core.ScriptCondComponent; @@ -71,9 +72,9 @@ public class FlowBus { return nodeMap.containsKey(nodeId); } - public static void addCommonNode(String nodeId, Node node) { + public static void addSpringScanNode(String nodeId, NodeComponent nodeComponent) { if (containNode(nodeId)) return; - nodeMap.put(nodeId, node); + 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 { @@ -82,8 +83,8 @@ public class FlowBus { addNode(nodeId, name, NodeTypeEnum.COMMON, cmpClazz, null); } - public static void addCommonNode(String nodeId, Class cmpClazz){ - addNode(nodeId, null, NodeTypeEnum.COMMON, cmpClazz, null); + public static void addCommonNode(String nodeId, String name, Class cmpClazz){ + addNode(nodeId, name, NodeTypeEnum.COMMON, cmpClazz, null); } public static void addCommonScriptNode(String nodeId, String name, String script){ @@ -106,10 +107,9 @@ public class FlowBus { LOG.warn("couldn't find component class [{}] from spring context", cmpClazz.getName()); cmpInstance = cmpClazz.newInstance(); } - cmpInstance.setNodeId(nodeId); - cmpInstance.setName(name); - cmpInstance.setSelf(cmpInstance); - cmpInstance.setType(type); + + //进行初始化 + cmpInstance = ComponentInitializer.loadInstance().initComponent(cmpInstance, type, name, nodeId); //如果是脚本节点(普通脚本节点/条件脚本节点),则还要加载script脚本 if (StrUtil.isNotBlank(script)){ diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java index b140798be..cb5f5b47d 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java @@ -52,7 +52,7 @@ public abstract class JsonFlowParser extends FlowParser { try { for (Map.Entry componentEntry : ComponentScanner.nodeComponentMap.entrySet()) { if (!FlowBus.containNode(componentEntry.getKey())) { - FlowBus.addCommonNode(componentEntry.getKey(), new Node(componentEntry.getValue())); + FlowBus.addSpringScanNode(componentEntry.getKey(), componentEntry.getValue()); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java index 6e4fa757f..e7f039863 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java @@ -58,7 +58,7 @@ public abstract class XmlFlowParser extends FlowParser { //先进行Spring上下文中的节点的判断 for (Entry componentEntry : ComponentScanner.nodeComponentMap.entrySet()) { if (!FlowBus.containNode(componentEntry.getKey())) { - FlowBus.addCommonNode(componentEntry.getKey(), new Node(componentEntry.getValue())); + FlowBus.addSpringScanNode(componentEntry.getKey(), componentEntry.getValue()); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java b/liteflow-core/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java index 64ffb37bc..8f05cf8f7 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java @@ -1,6 +1,7 @@ /** *

Title: liteflow

*

Description: 轻量级的组件式流程框架

+ * * @author Bryan.Zhang * @email weenyc31@163.com * @Date 2020/4/1 @@ -10,16 +11,20 @@ package com.yomahub.liteflow.spring; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.RetryCount; import com.yomahub.liteflow.aop.ICmpAroundAspect; +import com.yomahub.liteflow.core.ComponentInitializer; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.util.LOGOPrinter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * 组件扫描类,只要是NodeComponent的实现类,都可以被这个扫描器扫到 @@ -27,56 +32,43 @@ import java.util.Map; */ public class ComponentScanner implements BeanPostProcessor { - private static final Logger LOG = LoggerFactory.getLogger(ComponentScanner.class); + private static final Logger LOG = LoggerFactory.getLogger(ComponentScanner.class); - public static Map nodeComponentMap = new HashMap<>(); + public static Map nodeComponentMap = new HashMap<>(); - public static ICmpAroundAspect cmpAroundAspect; + public static ICmpAroundAspect cmpAroundAspect; - static { - // 打印liteflow的LOGO - LOGOPrinter.print(); - } + static { + // 打印liteflow的LOGO + LOGOPrinter.print(); + } - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } - @SuppressWarnings("rawtypes") - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - Class clazz = bean.getClass(); - // 组件的扫描发现,扫到之后缓存到类属性map中 - if (NodeComponent.class.isAssignableFrom(clazz)) { - LOG.info("component[{}] has been found", beanName); - NodeComponent nodeComponent = (NodeComponent) bean; - nodeComponent.setNodeId(beanName); + @SuppressWarnings("rawtypes") + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + Class clazz = bean.getClass(); + // 组件的扫描发现,扫到之后缓存到类属性map中 + if (NodeComponent.class.isAssignableFrom(clazz)) { + LOG.info("component[{}] has been found", beanName); + NodeComponent nodeComponent = (NodeComponent) bean; + nodeComponentMap.put(beanName, nodeComponent); + } - //判断NodeComponent是否是标识了@LiteflowComponent的标注 - //如果标注了,那么要从中取到name字段 - LiteflowComponent liteflowComponent = bean.getClass().getAnnotation(LiteflowComponent.class); - if (ObjectUtil.isNotNull(liteflowComponent)){ - String name = liteflowComponent.name(); - if (StrUtil.isNotBlank(name)){ - nodeComponent.setName(name); - } - } + // 组件Aop的实现类加载 + if (ICmpAroundAspect.class.isAssignableFrom(clazz)) { + LOG.info("component aspect implement[{}] has been found", beanName); + cmpAroundAspect = (ICmpAroundAspect) bean; + } - nodeComponent.setSelf(nodeComponent); - nodeComponentMap.put(beanName, nodeComponent); - } + return bean; + } - // 组件Aop的实现类加载 - if (ICmpAroundAspect.class.isAssignableFrom(clazz)) { - LOG.info("component aspect implement[{}] has been found", beanName); - cmpAroundAspect = (ICmpAroundAspect) bean; - } - - return bean; - } - - public static void cleanCache(){ - nodeComponentMap.clear(); - } + public static void cleanCache() { + nodeComponentMap.clear(); + } } diff --git a/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java b/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java index a1bb20fa9..a64a7910a 100644 --- a/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java +++ b/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java @@ -24,7 +24,7 @@ public class GroovyScriptExecutor implements ScriptExecutor { private ScriptEngine scriptEngine; - private Map compiledScriptMap = new HashMap<>(); + private final Map compiledScriptMap = new HashMap<>(); @Override public ScriptExecutor init() { diff --git a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/flowmeta/FlowMetaSpringbootTest.java b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/flowmeta/FlowMetaSpringbootTest.java index e6430e222..0641b6825 100644 --- a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/flowmeta/FlowMetaSpringbootTest.java +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/flowmeta/FlowMetaSpringbootTest.java @@ -30,9 +30,9 @@ public class FlowMetaSpringbootTest extends BaseTest { //测试动态添加元信息节点 @Test public void testFlowMeta() { - FlowBus.addCommonNode("d", DCmp.class); + FlowBus.addCommonNode("d", "d组件", DCmp.class); LiteflowResponse response= flowExecutor.execute2Resp("chain1", "it's a request"); Assert.assertTrue(response.isSuccess()); - Assert.assertEquals("a==>b==>c==>d", response.getSlot().printStep()); + Assert.assertEquals("a==>b==>c==>d[d组件]", response.getSlot().printStep()); } }