From 3ffeb014cd253af7440ad14c8edcdb6c1f1496d3 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Fri, 1 Dec 2017 18:23:09 +0800 Subject: [PATCH 01/41] =?UTF-8?q?=E4=BF=AE=E6=AD=A3pom=E7=9A=84=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E4=B8=8D=E5=AE=8C=E5=96=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 18 +++++++++++++++++- .../liteflow/test/TestWithSpringMain.java | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 27c0b52ba..eb833e15c 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ 3.4 4.1 2.4 + 1.2 4.2.6.RELEASE 1.7.21 1.2.17 @@ -32,7 +33,7 @@ org.apache.commons commons-collections4 - 4.1 + ${commons-collections.version} commons-io @@ -49,6 +50,16 @@ spring-context ${spring.version} + + org.springframework + spring-aop + ${spring.version} + + + org.springframework + spring-expression + ${spring.version} + org.springframework spring-test @@ -84,6 +95,11 @@ junit ${junit.version} + + commons-logging + commons-logging + ${commons-logging.version} + diff --git a/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java b/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java index 55de356f1..2a0c459ea 100644 --- a/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java +++ b/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java @@ -25,7 +25,7 @@ public class TestWithSpringMain { public void test1() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(10); - for(int i=0;i<100;i++){ + for(int i=0;i<1;i++){ executorService.submit(new Thread(){ @Override public void run() { From 1bd9c5c6cdf984052a090cc5702653eef8b43584 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 5 Feb 2018 19:34:34 +0800 Subject: [PATCH 02/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 93dc1efb3..f6ca617b3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,23 @@ ### 概述 -liteFlow是一个轻量级的组件式流程框架,帮助解耦业务代码,让每一个业务片段都是一个组件 +liteFlow是一个轻量,快速的组件式流程框架,帮助解耦业务代码,让每一个业务片段都是一个组件 * 提供本地xml的流程配置(后续全面支持spring式流程配置) * 提供基于spring的扫描方式注入component * 提供串行和并行2种模式。 -* 提供条件节点的模式。 * 消除组件之间参数传递,引入数据总线概念。 -* 自带简单的监控,能够知道每个组件的运行平均时间。消耗内存。(每隔10分钟会自动打印) +* 数据槽高并发隔离机制。 +* 提供无级嵌套条件节点的模式。 +* 自带简单的监控,能够知道每个组件的运行耗时排行(每隔5分钟会自动打印) + +### 最新版本1.3.1更新日志 +优化大量潜在的问题,此版本为稳定版本,主要更新点如下: +1. 增加条件节点功能 +2. 优化异常捕获的日志打印 +3. 支持自定义SLOT的特性 +4. 优化步骤打印,能够支持开闭区间的打印方式 +5. 增加了内部策略的调用方式 +6. 增加了追踪ID +7. 优化了监控打印 ### Quick Start 1. 定义组件需继承Component,项目启动时会被自动发现。 From 2fcfccadcca98280267ca5bc04f2dc7ad077f764 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 12 Feb 2018 16:53:32 +0800 Subject: [PATCH 03/41] =?UTF-8?q?=E5=8A=A0=E5=85=A5logo=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/core/FlowExecutor.java | 1 + .../liteflow/spring/ComponentScaner.java | 5 ++++ .../liteflow/util/LOGOPrinter.java | 23 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java diff --git a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java index 87d73276b..57c48c9c4 100644 --- a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java +++ b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java @@ -33,6 +33,7 @@ import com.thebeastshop.liteflow.exception.FlowSystemException; import com.thebeastshop.liteflow.exception.NoAvailableSlotException; import com.thebeastshop.liteflow.flow.FlowBus; import com.thebeastshop.liteflow.parser.FlowParser; +import com.thebeastshop.liteflow.util.LOGOPrinter; public class FlowExecutor { diff --git a/src/main/java/com/thebeastshop/liteflow/spring/ComponentScaner.java b/src/main/java/com/thebeastshop/liteflow/spring/ComponentScaner.java index 81c74891a..60c3993f1 100644 --- a/src/main/java/com/thebeastshop/liteflow/spring/ComponentScaner.java +++ b/src/main/java/com/thebeastshop/liteflow/spring/ComponentScaner.java @@ -19,6 +19,7 @@ import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import com.thebeastshop.liteflow.core.NodeComponent; import com.thebeastshop.liteflow.entity.config.Node; +import com.thebeastshop.liteflow.util.LOGOPrinter; public class ComponentScaner implements BeanPostProcessor, PriorityOrdered { @@ -26,6 +27,10 @@ public class ComponentScaner implements BeanPostProcessor, PriorityOrdered { public static Map nodeComponentMap = new HashMap(); + static { + LOGOPrinter.print(); + } + @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; diff --git a/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java b/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java new file mode 100644 index 000000000..7c7e470bd --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java @@ -0,0 +1,23 @@ +package com.thebeastshop.liteflow.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LOGOPrinter { + + private static final Logger LOG = LoggerFactory.getLogger(LOGOPrinter.class); + + public static void print() { + StringBuilder str = new StringBuilder("\n"); + str.append("================================================================================================\n"); + str.append(" _ ___ _____ _____ _____ _ _____ __\n"); + str.append(" | | |_ _|_ _| ____| | ___| | / _ \\ \\ / /\n"); + str.append(" | | | | | | | _| _____| |_ | | | | | \\ \\ /\\ / / \n"); + str.append(" | |___ | | | | | |__|_____| _| | |__| |_| |\\ V V / \n"); + str.append(" |_____|___| |_| |_____| |_| |_____\\___/ \\_/\\_/ \n\n"); + str.append(" 做最轻量级,最吊炸天的微流程框架\n"); + str.append(" To be the most lightweight and the most practical micro-process framework\n"); + str.append("================================================================================================\n"); + LOG.info(str.toString()); + } +} From bf9cd9fb7644eca192bfc08a48839c2bd658d743 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 12 Feb 2018 16:55:25 +0800 Subject: [PATCH 04/41] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=B4=E6=97=B6?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/parser/TempConvert.java | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 src/main/java/com/thebeastshop/liteflow/parser/TempConvert.java diff --git a/src/main/java/com/thebeastshop/liteflow/parser/TempConvert.java b/src/main/java/com/thebeastshop/liteflow/parser/TempConvert.java deleted file mode 100644 index b8f4818a6..000000000 --- a/src/main/java/com/thebeastshop/liteflow/parser/TempConvert.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.thebeastshop.liteflow.parser; - -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 类型转换 - * @author gongjun[jun.gong@thebeastshop.com] - * @since 2017-11-22 15:13 - */ -@Component -public class TempConvert { - - - - private static List match(String input) { - List list = new ArrayList(); - Stack stack = new Stack<>(); - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - if (c == '(') { - stack.push(c); - if (stack.size() == 1 && buffer.length() > 0) { - list.add(buffer.toString()); - buffer = new StringBuffer(); - }else { - buffer.append(c); - } - }else if (c == ')') { - if (stack.size() > 0) { - stack.pop(); - if (stack.size() == 0) { - if (buffer.length() > 0) { - list.add(buffer.toString()); - buffer = new StringBuffer(); - } - }else { - buffer.append(c); - } - } - }else { - buffer.append(c); - } - } - if (buffer.length() > 0) { - list.add(buffer.toString()); - } - return list; - } - - - public static void main(String[] args) { - List list = new ArrayList(); - String input = "aaaa(bbb(xxxxx|yyyy))"; - list = match(input); - System.out.println(list); - } -} From 38f6addfd61567a799cf169d276567d2ec0e37b6 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 26 Feb 2018 20:48:32 +0800 Subject: [PATCH 05/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=B9zk?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 + .../liteflow/core/FlowExecutor.java | 37 ++++- .../liteflow/core/NodeComponent.java | 8 +- .../liteflow/exception/ParseException.java | 21 +++ .../thebeastshop/liteflow/flow/FlowBus.java | 14 ++ .../liteflow/parser/LocalXmlFlowParser.java | 22 +++ .../liteflow/parser/RegexEntity.java | 32 +++++ .../{FlowParser.java => XmlFlowParser.java} | 81 ++--------- .../parser/ZookeeperXmlFlowParser.java | 63 ++++++++ .../liteflow/util/LOGOPrinter.java | 2 +- .../liteflow/test/TestWithSpringMain.java | 15 +- .../liteflow/test/curator/CuratorTest.java | 135 ++++++++++++++++++ .../liteflow/test/curator/CuratorTest2.java | 121 ++++++++++++++++ .../liteflow/test/regex/RegexTest.java | 23 +++ src/test/resources/spring-test.xml | 12 +- 15 files changed, 513 insertions(+), 79 deletions(-) create mode 100644 src/main/java/com/thebeastshop/liteflow/exception/ParseException.java create mode 100644 src/main/java/com/thebeastshop/liteflow/parser/LocalXmlFlowParser.java create mode 100644 src/main/java/com/thebeastshop/liteflow/parser/RegexEntity.java rename src/main/java/com/thebeastshop/liteflow/parser/{FlowParser.java => XmlFlowParser.java} (67%) create mode 100644 src/main/java/com/thebeastshop/liteflow/parser/ZookeeperXmlFlowParser.java create mode 100644 src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest.java create mode 100644 src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest2.java create mode 100644 src/test/java/com/thebeastshop/liteflow/test/regex/RegexTest.java diff --git a/pom.xml b/pom.xml index 4ddc95628..1b490e37e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 1.7.13 1.2.7 1.6.1 + 2.11.1 4.12 @@ -100,6 +101,11 @@ commons-logging ${commons-logging.version} + + org.apache.curator + curator-recipes + ${curator.version} + diff --git a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java index 57c48c9c4..8831592b9 100644 --- a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java +++ b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java @@ -13,6 +13,8 @@ import java.text.MessageFormat; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -27,12 +29,14 @@ import com.thebeastshop.liteflow.entity.data.DataBus; import com.thebeastshop.liteflow.entity.data.DefaultSlot; import com.thebeastshop.liteflow.entity.data.Slot; import com.thebeastshop.liteflow.exception.ChainNotFoundException; -import com.thebeastshop.liteflow.exception.ComponentNotAccessException; import com.thebeastshop.liteflow.exception.FlowExecutorNotInitException; import com.thebeastshop.liteflow.exception.FlowSystemException; import com.thebeastshop.liteflow.exception.NoAvailableSlotException; +import com.thebeastshop.liteflow.exception.ParseException; import com.thebeastshop.liteflow.flow.FlowBus; -import com.thebeastshop.liteflow.parser.FlowParser; +import com.thebeastshop.liteflow.parser.LocalXmlFlowParser; +import com.thebeastshop.liteflow.parser.XmlFlowParser; +import com.thebeastshop.liteflow.parser.ZookeeperXmlFlowParser; import com.thebeastshop.liteflow.util.LOGOPrinter; public class FlowExecutor { @@ -41,17 +45,36 @@ public class FlowExecutor { private List rulePath; + private String zkNode; + public void init() { + XmlFlowParser parser = null; for(String path : rulePath){ try { - FlowParser.parseLocal(path); + if(isZKConfig(path)) { + if(StringUtils.isNotBlank(zkNode)) { + parser = new ZookeeperXmlFlowParser(zkNode); + }else { + parser = new ZookeeperXmlFlowParser(); + } + }else { + parser = new LocalXmlFlowParser(); + } + parser.parseMain(path); } catch (Exception e) { String errorMsg = MessageFormat.format("init flow executor cause error,cannot parse rule file{0}", path); + LOG.error(errorMsg,e); throw new FlowExecutorNotInitException(errorMsg); } } } + private boolean isZKConfig(String path) { + Pattern p = Pattern.compile("[\\w\\d][\\w\\d\\.]+\\:(\\d)+(\\,[\\w\\d][\\w\\d\\.]+\\:(\\d)+)*"); + Matcher m = p.matcher(path); + return m.find(); + } + public void reloadRule(){ init(); } @@ -200,4 +223,12 @@ public class FlowExecutor { public void setRulePath(List rulePath) { this.rulePath = rulePath; } + + public String getZkNode() { + return zkNode; + } + + public void setZkNode(String zkNode) { + this.zkNode = zkNode; + } } diff --git a/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java b/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java index 0f77eb276..151f951bd 100644 --- a/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java +++ b/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java @@ -20,8 +20,9 @@ import com.thebeastshop.liteflow.entity.data.CmpStepType; import com.thebeastshop.liteflow.entity.data.DataBus; import com.thebeastshop.liteflow.entity.data.Slot; import com.thebeastshop.liteflow.entity.monitor.CompStatistics; +import com.thebeastshop.liteflow.flow.FlowBus; import com.thebeastshop.liteflow.monitor.MonitorBus; -import com.thebeastshop.liteflow.parser.FlowParser; +import com.thebeastshop.liteflow.parser.LocalXmlFlowParser; public abstract class NodeComponent { @@ -37,12 +38,11 @@ public abstract class NodeComponent { slot.addStep(new CmpStep(nodeId, CmpStepType.START)); StopWatch stopWatch = new StopWatch(); stopWatch.start(); - long initm=Runtime.getRuntime().freeMemory(); process(); + stopWatch.stop(); long timeSpent = stopWatch.getTime(); - long endm=Runtime.getRuntime().freeMemory(); slot.addStep(new CmpStep(nodeId, CmpStepType.END)); @@ -56,7 +56,7 @@ public abstract class NodeComponent { if(this instanceof NodeCondComponent){ String condNodeId = slot.getCondResult(this.getClass().getName()); if(StringUtils.isNotBlank(condNodeId)){ - Node thisNode = FlowParser.getNode(nodeId); + Node thisNode = FlowBus.getNode(nodeId); Node condNode = thisNode.getCondNode(condNodeId); if(condNode != null){ NodeComponent condComponent = condNode.getInstance(); diff --git a/src/main/java/com/thebeastshop/liteflow/exception/ParseException.java b/src/main/java/com/thebeastshop/liteflow/exception/ParseException.java new file mode 100644 index 000000000..99917bb13 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/exception/ParseException.java @@ -0,0 +1,21 @@ +package com.thebeastshop.liteflow.exception; + +public class ParseException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** 异常信息 */ + private String message; + + public ParseException(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java b/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java index d8130c33f..ce799e588 100644 --- a/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java +++ b/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java @@ -15,11 +15,14 @@ import java.util.Map; import org.apache.commons.collections4.MapUtils; import com.thebeastshop.liteflow.entity.config.Chain; +import com.thebeastshop.liteflow.entity.config.Node; public class FlowBus { private static Map chainMap; + private static Map nodeMap; + public static Chain getChain(String id) throws Exception{ if(chainMap == null || chainMap.isEmpty()){ throw new Exception("please config the rule first"); @@ -37,4 +40,15 @@ public class FlowBus { public static boolean needInit() { return MapUtils.isEmpty(chainMap); } + + public static void addNode(String nodeId, Node node) { + if(nodeMap == null) { + nodeMap = new HashMap(); + } + nodeMap.put(nodeId, node); + } + + public static Node getNode(String nodeId) { + return nodeMap.get(nodeId); + } } diff --git a/src/main/java/com/thebeastshop/liteflow/parser/LocalXmlFlowParser.java b/src/main/java/com/thebeastshop/liteflow/parser/LocalXmlFlowParser.java new file mode 100644 index 000000000..a8a929bf0 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/parser/LocalXmlFlowParser.java @@ -0,0 +1,22 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.parser; + +import com.thebeastshop.liteflow.util.IOUtil; + +public class LocalXmlFlowParser extends XmlFlowParser{ + + private final String ENCODING_FORMAT = "UTF-8"; + + public void parseMain(String rulePath) throws Exception { + String ruleContent = IOUtil.read(rulePath, ENCODING_FORMAT); + parse(ruleContent); + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/parser/RegexEntity.java b/src/main/java/com/thebeastshop/liteflow/parser/RegexEntity.java new file mode 100644 index 000000000..55ffd84ee --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/parser/RegexEntity.java @@ -0,0 +1,32 @@ +package com.thebeastshop.liteflow.parser; + +import java.util.Arrays; + +public class RegexEntity { + + private String condNode; + + private String[] realNodeArray; + + public String getCondNode() { + return condNode; + } + + public void setCondNode(String condNode) { + this.condNode = condNode; + } + + public String[] getRealNodeArray() { + return realNodeArray; + } + + public void setRealNodeArray(String[] realNodeArray) { + this.realNodeArray = realNodeArray; + } + + @Override + public String toString() { + return "RegexEntity [condNode=" + condNode + ", realNodeArray=" + + Arrays.toString(realNodeArray) + "]"; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java b/src/main/java/com/thebeastshop/liteflow/parser/XmlFlowParser.java similarity index 67% rename from src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java rename to src/main/java/com/thebeastshop/liteflow/parser/XmlFlowParser.java index 6537e376c..ef84df3aa 100644 --- a/src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java +++ b/src/main/java/com/thebeastshop/liteflow/parser/XmlFlowParser.java @@ -1,20 +1,8 @@ -/** - *

Title: liteFlow

- *

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

- *

Copyright: Copyright (c) 2017

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2017-7-28 - * @version 1.0 - */ package com.thebeastshop.liteflow.parser; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -34,28 +22,20 @@ import com.thebeastshop.liteflow.entity.config.WhenCondition; import com.thebeastshop.liteflow.flow.FlowBus; import com.thebeastshop.liteflow.spring.ComponentScaner; import com.thebeastshop.liteflow.util.Dom4JReader; -import com.thebeastshop.liteflow.util.IOUtil; -@SuppressWarnings("unchecked") -public class FlowParser { - - private static final Logger LOG = LoggerFactory.getLogger(FlowParser.class); - - private static final String ENCODING_FORMAT = "UTF-8"; +public abstract class XmlFlowParser { - private static Map nodeMap = new HashMap(); - - public static void parseLocal(String rulePath) throws Exception { - String ruleContent = IOUtil.read(rulePath, ENCODING_FORMAT); - parse(ruleContent); - } - - public static void parse(String content) throws Exception { + private final Logger LOG = LoggerFactory.getLogger(XmlFlowParser.class); + + public abstract void parseMain(String path) throws Exception; + + public void parse(String content) throws Exception { Document document = Dom4JReader.getFormatDocument(content); parse(document); } - public static void parse(Document document) throws Exception { + @SuppressWarnings("unchecked") + public void parse(Document document) throws Exception { try { Element rootElement = document.getRootElement(); @@ -79,11 +59,11 @@ public class FlowParser { } component.setNodeId(id); node.setInstance(component); - nodeMap.put(id, node); + FlowBus.addNode(id, node); } }else{ for(Entry componentEntry : ComponentScaner.nodeComponentMap.entrySet()){ - nodeMap.put(componentEntry.getKey(), new Node(componentEntry.getKey(), componentEntry.getValue().getClass().getName(), componentEntry.getValue())); + FlowBus.addNode(componentEntry.getKey(), new Node(componentEntry.getKey(), componentEntry.getValue().getClass().getName(), componentEntry.getValue())); } } @@ -110,11 +90,11 @@ public class FlowParser { Node node = null; for (int i = 0; i < condArray.length; i++) { regexEntity = parseNodeStr(condArray[i].trim()); - node = nodeMap.get(regexEntity.getCondNode()); + node = FlowBus.getNode(regexEntity.getCondNode()); chainNodeList.add(node); if(regexEntity.getRealNodeArray() != null){ for(String key : regexEntity.getRealNodeArray()){ - Node condNode = nodeMap.get(key); + Node condNode = FlowBus.getNode(key); if(condNode != null){ node.setCondNode(condNode.getId(), condNode); } @@ -135,39 +115,6 @@ public class FlowParser { } - public static Node getNode(String nodeId){ - return nodeMap.get(nodeId); - } - - private static class RegexEntity{ - - private String condNode; - - private String[] realNodeArray; - - public String getCondNode() { - return condNode; - } - - public void setCondNode(String condNode) { - this.condNode = condNode; - } - - public String[] getRealNodeArray() { - return realNodeArray; - } - - public void setRealNodeArray(String[] realNodeArray) { - this.realNodeArray = realNodeArray; - } - - @Override - public String toString() { - return "RegexEntity [condNode=" + condNode + ", realNodeArray=" - + Arrays.toString(realNodeArray) + "]"; - } - } - public static RegexEntity parseNodeStr(String str) { List list = new ArrayList(); Pattern p = Pattern.compile("[^\\)\\(]+"); @@ -186,8 +133,4 @@ public class FlowParser { } return regexEntity; } - - public static void main(String[] args) { - System.out.println(parseNodeStr("aaaa ( xxxx | yyyy | vvvv )")); - } } diff --git a/src/main/java/com/thebeastshop/liteflow/parser/ZookeeperXmlFlowParser.java b/src/main/java/com/thebeastshop/liteflow/parser/ZookeeperXmlFlowParser.java new file mode 100644 index 000000000..047aca383 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/parser/ZookeeperXmlFlowParser.java @@ -0,0 +1,63 @@ +package com.thebeastshop.liteflow.parser; + +import java.text.MessageFormat; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.NodeCache; +import org.apache.curator.framework.recipes.cache.NodeCacheListener; +import org.apache.curator.retry.RetryNTimes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.thebeastshop.liteflow.exception.ParseException; + +public class ZookeeperXmlFlowParser extends XmlFlowParser{ + + private static final Logger LOG = LoggerFactory.getLogger(ZookeeperXmlFlowParser.class); + + private String nodePath = "/lite-flow/flow"; + + public ZookeeperXmlFlowParser() { + + } + + public ZookeeperXmlFlowParser(String node) { + nodePath = node; + } + + @Override + public void parseMain(String path) throws Exception { + CuratorFramework client = CuratorFrameworkFactory.newClient( + path, + new RetryNTimes(10, 5000) + ); + client.start(); + + if (client.checkExists().forPath(nodePath) == null) { + client.create().creatingParentsIfNeeded().forPath(nodePath, "".getBytes()); + } + + String content = new String(client.getData().forPath(nodePath)); + + + if(StringUtils.isBlank(content)) { + String error = MessageFormat.format("the node[{0}] value is empty", nodePath); + throw new ParseException(error); + } + parse(content); + + + final NodeCache cache = new NodeCache(client,nodePath); + cache.start(); + + cache.getListenable().addListener(new NodeCacheListener() { + @Override + public void nodeChanged() throws Exception { + String content = new String(cache.getCurrentData().getData()); + LOG.info("stating load flow config...."); + parse(content); + } + }); + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java b/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java index 7c7e470bd..1bc27212f 100644 --- a/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java +++ b/src/main/java/com/thebeastshop/liteflow/util/LOGOPrinter.java @@ -15,7 +15,7 @@ public class LOGOPrinter { str.append(" | | | | | | | _| _____| |_ | | | | | \\ \\ /\\ / / \n"); str.append(" | |___ | | | | | |__|_____| _| | |__| |_| |\\ V V / \n"); str.append(" |_____|___| |_| |_____| |_| |_____\\___/ \\_/\\_/ \n\n"); - str.append(" 做最轻量级,最吊炸天的微流程框架\n"); + str.append(" 做最轻量级,最实用的微流程框架\n"); str.append(" To be the most lightweight and the most practical micro-process framework\n"); str.append("================================================================================================\n"); LOG.info(str.toString()); diff --git a/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java b/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java index a5e7f777e..61897ce48 100644 --- a/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java +++ b/src/test/java/com/thebeastshop/liteflow/test/TestWithSpringMain.java @@ -19,7 +19,7 @@ import com.thebeastshop.liteflow.entity.data.Slot; @ContextConfiguration(locations = { "classpath:spring-test.xml" }) public class TestWithSpringMain { - @Resource + @Resource(name="flowExecutor") private FlowExecutor flowExecutor; @Test @@ -50,4 +50,17 @@ public class TestWithSpringMain { } } + + @Test + public void test3() throws Exception { + try { + while(true) { + Slot slot = flowExecutor.execute("chain3", "it's a request"); + Thread.sleep(2000); + } + }catch(Exception e) { + e.printStackTrace(); + } + + } } diff --git a/src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest.java b/src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest.java new file mode 100644 index 000000000..3d6989dcc --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest.java @@ -0,0 +1,135 @@ +package com.thebeastshop.liteflow.test.curator; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.NodeCache; +import org.apache.curator.framework.recipes.cache.NodeCacheListener; +import org.apache.curator.framework.recipes.cache.PathChildrenCache; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; +import org.apache.curator.retry.RetryNTimes; + +public class CuratorTest { + + /** Zookeeper info */ + private static final String ZK_ADDRESS = "123.206.92.144:2181,123.206.92.144:2182,123.206.92.144:2183"; + private static final String ZK_PATH = "/zktest/a1/aa1"; + + public static void main(String[] args) throws Exception { + // 1.Connect to zk + CuratorFramework client = CuratorFrameworkFactory.newClient( + ZK_ADDRESS, + new RetryNTimes(10, 5000) + ); + client.start(); + + checkNode(client); + +// childNodeListen(client); + +// removeNodeData(client); + +// createNode(client); + +// nodeListen(client); +// +// modifyNodeData(client); + + System.in.read(); + +// getNodeData(client); +// +// + + } + + private static void checkNode(CuratorFramework client) throws Exception { + System.out.println(client.checkExists().forPath("/test")); + } + + private static void createNode(CuratorFramework client) throws Exception { + String data1 = "nice to meet you"; + print("create", ZK_PATH, data1); + client.create(). + creatingParentsIfNeeded(). + forPath(ZK_PATH, data1.getBytes()); + } + + private static void getNodeData(CuratorFramework client) throws Exception { + print("ls", "/"); + print(client.getChildren().forPath("/")); + print("get", ZK_PATH); + print(client.getData().forPath(ZK_PATH)); + } + + private static void modifyNodeData(CuratorFramework client) throws Exception { + String data2 = "world for u"; + print("set", ZK_PATH, data2); + client.setData().forPath(ZK_PATH, data2.getBytes()); + print("get", ZK_PATH); + print(client.getData().forPath(ZK_PATH)); + } + + private static void removeNodeData(CuratorFramework client) throws Exception { + print("delete", "/zktest/dddd"); + client.delete().forPath("/zktest/dddd"); + print("ls", "/"); + print(client.getChildren().forPath("/")); + } + + private static void nodeListen(CuratorFramework client) throws Exception { + final NodeCache cache = new NodeCache(client,ZK_PATH); + cache.start(); + + cache.getListenable().addListener(new NodeCacheListener() { + + @Override + public void nodeChanged() throws Exception { + byte[] res = cache.getCurrentData().getData(); + System.out.println("data: " + new String(res)); + } + }); + } + + private static void childNodeListen(CuratorFramework client) throws Exception { + final PathChildrenCache cache = new PathChildrenCache(client,"/zktest",true); + cache.start(); + + cache.getListenable().addListener(new PathChildrenCacheListener() { + + @Override + public void childEvent(CuratorFramework curator, PathChildrenCacheEvent event) throws Exception { + switch (event.getType()) { + case CHILD_ADDED: + System.out.println("add:" + event.getData().getPath() + ":" + new String(event.getData().getData())); + break; + case CHILD_UPDATED: + System.out.println("update:" + event.getData().getPath() + ":" + new String(event.getData().getData())); + break; + case CHILD_REMOVED: + System.out.println("remove:" + event.getData().getPath() + ":" + new String(event.getData().getData())); + break; + default: + break; + } + } + }); + } + + + private static void print(String... cmds) { + StringBuilder text = new StringBuilder("$ "); + for (String cmd : cmds) { + text.append(cmd).append(" "); + } + System.out.println(text.toString()); + } + + private static void print(Object result) { + System.out.println( + result instanceof byte[] + ? new String((byte[]) result) + : result); + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest2.java b/src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest2.java new file mode 100644 index 000000000..232ecace1 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/curator/CuratorTest2.java @@ -0,0 +1,121 @@ +package com.thebeastshop.liteflow.test.curator; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.NodeCache; +import org.apache.curator.framework.recipes.cache.NodeCacheListener; +import org.apache.curator.framework.recipes.cache.PathChildrenCache; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; +import org.apache.curator.retry.RetryNTimes; + +public class CuratorTest2 { + + /** Zookeeper info */ + private static final String ZK_ADDRESS = "114.55.174.189:2181"; + private static final String ZK_PATH = "/zktest/ffff"; + + public static void main(String[] args) throws Exception { + // 1.Connect to zk + CuratorFramework client = CuratorFrameworkFactory.newClient( + ZK_ADDRESS, + new RetryNTimes(10, 5000) + ); + client.start(); + +// removeNodeData(client); + +// createNode(client); + +// nodeListen(client); +// + modifyNodeData(client); + + } + + private static void createNode(CuratorFramework client) throws Exception { + String data1 = "hello"; + print("create", ZK_PATH, data1); + client.create(). + creatingParentsIfNeeded(). + forPath(ZK_PATH, data1.getBytes()); + } + + private static void getNodeData(CuratorFramework client) throws Exception { + print("ls", "/"); + print(client.getChildren().forPath("/")); + print("get", ZK_PATH); + print(client.getData().forPath(ZK_PATH)); + } + + private static void modifyNodeData(CuratorFramework client) throws Exception { + String data2 = "world for u"; + print("set", ZK_PATH, data2); + client.setData().forPath(ZK_PATH, data2.getBytes()); + print("get", ZK_PATH); + print(client.getData().forPath(ZK_PATH)); + } + + private static void removeNodeData(CuratorFramework client) throws Exception { + print("delete", "/zktest/dddd"); + client.delete().forPath("/zktest/dddd"); + print("ls", "/"); + print(client.getChildren().forPath("/")); + } + + private static void nodeListen(CuratorFramework client) throws Exception { + final NodeCache cache = new NodeCache(client,ZK_PATH); + cache.start(); + + cache.getListenable().addListener(new NodeCacheListener() { + + @Override + public void nodeChanged() throws Exception { + byte[] res = cache.getCurrentData().getData(); + System.out.println("data: " + new String(res)); + } + }); + } + + private static void childNodeListen(CuratorFramework client) throws Exception { + final PathChildrenCache cache = new PathChildrenCache(client,"/zktest",true); + cache.start(); + + cache.getListenable().addListener(new PathChildrenCacheListener() { + + @Override + public void childEvent(CuratorFramework curator, PathChildrenCacheEvent event) throws Exception { + switch (event.getType()) { + case CHILD_ADDED: + System.out.println("add:" + event.getData().getPath() + ":" + new String(event.getData().getData())); + break; + case CHILD_UPDATED: + System.out.println("update:" + event.getData().getPath() + ":" + new String(event.getData().getData())); + break; + case CHILD_REMOVED: + System.out.println("remove:" + event.getData().getPath() + ":" + new String(event.getData().getData())); + break; + default: + break; + } + } + }); + } + + + private static void print(String... cmds) { + StringBuilder text = new StringBuilder("$ "); + for (String cmd : cmds) { + text.append(cmd).append(" "); + } + System.out.println(text.toString()); + } + + private static void print(Object result) { + System.out.println( + result instanceof byte[] + ? new String((byte[]) result) + : result); + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/regex/RegexTest.java b/src/test/java/com/thebeastshop/liteflow/test/regex/RegexTest.java new file mode 100644 index 000000000..c1efeebd9 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/regex/RegexTest.java @@ -0,0 +1,23 @@ +package com.thebeastshop.liteflow.test.regex; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RegexTest { + + public static void main(String[] args) { + String str = "192.168.1.1:2181,192.168.1.2:2182,192.168.1.3:2183"; + List list = new ArrayList(); + Pattern p = Pattern.compile("[\\w\\d][\\w\\d\\.]+\\:(\\d)+(\\,[\\w\\d][\\w\\d\\.]+\\:(\\d)+)*"); + Matcher m = p.matcher(str); + while(m.find()){ + list.add(m.group()); + } + System.out.println(list.size()); + System.out.println(list); + + } + +} diff --git a/src/test/resources/spring-test.xml b/src/test/resources/spring-test.xml index 685098480..71ba484c4 100644 --- a/src/test/resources/spring-test.xml +++ b/src/test/resources/spring-test.xml @@ -10,11 +10,21 @@ - + + + + + + + 123.206.92.144:2181,123.206.92.144:2182,123.206.92.144:2183 + + + \ No newline at end of file From 76ae12dfe5276656a734376632a25c31326157d1 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 26 Feb 2018 20:49:40 +0800 Subject: [PATCH 06/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/spring-test.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/test/resources/spring-test.xml b/src/test/resources/spring-test.xml index 71ba484c4..b5ad909fc 100644 --- a/src/test/resources/spring-test.xml +++ b/src/test/resources/spring-test.xml @@ -10,21 +10,22 @@ - + config/flow.xml - --> + - + - + 这个不配置就用默认的/lite-flow/flow节点 + --> \ No newline at end of file From 6bf72e36553878d4a6b024a111efd9763c9595f1 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Tue, 27 Feb 2018 15:18:59 +0800 Subject: [PATCH 07/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89parser=E7=9A=84=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/core/FlowExecutor.java | 21 ++++++++++++++++--- .../liteflow/parser/ClassXmlFlowParser.java | 11 ++++++++++ .../liteflow/test/TestCustomParser.java | 13 ++++++++++++ src/test/resources/spring-test.xml | 9 ++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/thebeastshop/liteflow/parser/ClassXmlFlowParser.java create mode 100644 src/test/java/com/thebeastshop/liteflow/test/TestCustomParser.java diff --git a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java index 8831592b9..4a3620f37 100644 --- a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java +++ b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java @@ -51,14 +51,17 @@ public class FlowExecutor { XmlFlowParser parser = null; for(String path : rulePath){ try { - if(isZKConfig(path)) { + if(isLocalConfig(path)) { + parser = new LocalXmlFlowParser(); + }else if(isZKConfig(path)){ if(StringUtils.isNotBlank(zkNode)) { parser = new ZookeeperXmlFlowParser(zkNode); }else { parser = new ZookeeperXmlFlowParser(); } - }else { - parser = new LocalXmlFlowParser(); + }else if(isClassConfig(path)) { + Class c = Class.forName(path); + parser = (XmlFlowParser)c.newInstance(); } parser.parseMain(path); } catch (Exception e) { @@ -75,6 +78,18 @@ public class FlowExecutor { return m.find(); } + private boolean isLocalConfig(String path) { + Pattern p = Pattern.compile("^[\\w\\/]+(\\/\\w+)*\\.xml$"); + Matcher m = p.matcher(path); + return m.find(); + } + + private boolean isClassConfig(String path) { + Pattern p = Pattern.compile("^\\w+(\\.\\w+)*$"); + Matcher m = p.matcher(path); + return m.find(); + } + public void reloadRule(){ init(); } diff --git a/src/main/java/com/thebeastshop/liteflow/parser/ClassXmlFlowParser.java b/src/main/java/com/thebeastshop/liteflow/parser/ClassXmlFlowParser.java new file mode 100644 index 000000000..692b47e03 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/parser/ClassXmlFlowParser.java @@ -0,0 +1,11 @@ +package com.thebeastshop.liteflow.parser; + +public abstract class ClassXmlFlowParser extends XmlFlowParser { + @Override + public void parseMain(String path) throws Exception { + String content = parseCustom(); + parse(content); + } + + public abstract String parseCustom(); +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/TestCustomParser.java b/src/test/java/com/thebeastshop/liteflow/test/TestCustomParser.java new file mode 100644 index 000000000..652a90666 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/TestCustomParser.java @@ -0,0 +1,13 @@ +package com.thebeastshop.liteflow.test; + +import com.thebeastshop.liteflow.parser.ClassXmlFlowParser; + +public class TestCustomParser extends ClassXmlFlowParser { + + @Override + public String parseCustom() { + System.out.println("进入自定义parser,这里只做进入作用,不返回具体xml"); + return null; + } + +} diff --git a/src/test/resources/spring-test.xml b/src/test/resources/spring-test.xml index b5ad909fc..26ff8018a 100644 --- a/src/test/resources/spring-test.xml +++ b/src/test/resources/spring-test.xml @@ -28,4 +28,13 @@ 这个不配置就用默认的/lite-flow/flow节点 --> + + + \ No newline at end of file From 9928f9228f84da53739d28444d8158d6b5c9eb62 Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Tue, 27 Feb 2018 18:26:09 +0800 Subject: [PATCH 08/41] Set theme jekyll-theme-hacker --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 000000000..fc24e7a62 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file From 33d917cdffa94dbfd456b43822bc79f117c72224 Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Tue, 27 Feb 2018 18:28:32 +0800 Subject: [PATCH 09/41] Set theme jekyll-theme-minimal --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index fc24e7a62..2f7efbeab 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-hacker \ No newline at end of file +theme: jekyll-theme-minimal \ No newline at end of file From 6a8d2a02184f098c0b021a99b8eb936077b6b095 Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Tue, 27 Feb 2018 18:29:31 +0800 Subject: [PATCH 10/41] Set theme jekyll-theme-midnight --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 2f7efbeab..18854876c 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-minimal \ No newline at end of file +theme: jekyll-theme-midnight \ No newline at end of file From 2ecbea102733fe61710497cca57b35977a190446 Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Tue, 27 Feb 2018 18:31:27 +0800 Subject: [PATCH 11/41] Set theme jekyll-theme-cayman --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 18854876c..c4192631f 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-midnight \ No newline at end of file +theme: jekyll-theme-cayman \ No newline at end of file From 0645bcbcd8c83bb3cf24a817d00025864bd26c40 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 16:11:10 +0800 Subject: [PATCH 12/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=88=B0v2.0,=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/architecture.md | 1 + docs/images/architecture.png | Bin 0 -> 50146 bytes docs/quickstart.md | 62 +++++++++++++++++++++++++++++++++++ docs/runwithcustom.md | 29 ++++++++++++++++ docs/runwithspring.md | 34 +++++++++++++++++++ docs/runwithzookeeper.md | 20 +++++++++++ pom.xml | 2 +- 7 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 docs/architecture.md create mode 100644 docs/images/architecture.png create mode 100644 docs/quickstart.md create mode 100644 docs/runwithcustom.md create mode 100644 docs/runwithspring.md create mode 100644 docs/runwithzookeeper.md diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 000000000..fa7cc9cb6 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1 @@ +### \ No newline at end of file diff --git a/docs/images/architecture.png b/docs/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..e0cf70d4ce41c250b68c7224b9ba437971417f22 GIT binary patch literal 50146 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 + com.thebeastshop.liteflow + liteflow + ${liteFlow.version} + +``` +## 流程配置文件 +```xml + + + + + + + + + + + + + + + +``` + +component为组件,这里你需要实现这些组件,每个组件继承`NodeComponent`类 +```java +public class AComponent extends NodeComponent { + + @Override + public void process() { + String str = this.getSlot().getRequestData(); + System.out.println(str); + System.out.println("Acomponent executed!"); + } +} +``` + +chain为流程链,每个链上可配置多个组件节点。目前执行的模式分串行和并行2种。 +串行标签为`then`,并行标签为`when`。 +在串行的模式下,以下2种写法是等价的,可以根据业务需要来把不同种类的节点放一行里。 +```xml + +``` +```xml + + +``` + +## 执行流程链 +```java +FlowExecutor executor = new FlowExecutor(); +executor.setRulePath(Arrays.asList(new String[]{"/config/flow.xml"})); +executor.init(); +Slot slot = executor.execute("demoChain", "arg"); +``` + +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/runwithspring) \ No newline at end of file diff --git a/docs/runwithcustom.md b/docs/runwithcustom.md new file mode 100644 index 000000000..71963b000 --- /dev/null +++ b/docs/runwithcustom.md @@ -0,0 +1,29 @@ +## 使用自定义的方式配置 +如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置的扩展点。 + +# 创建自定义的配置的类 +在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 +```java +public class TestCustomParser extends ClassXmlFlowParser { + + @Override + public String parseCustom() { + System.out.println("进入自定义parser"); + String xmlContent = null; + //这里需要自己扩展从自定义的地方获取配置 + return xmlContent; + } +} +``` + +# spring配置 +spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 +```xml + + + + com.thebeastshop.liteflow.test.TestCustomParser + + + +``` \ No newline at end of file diff --git a/docs/runwithspring.md b/docs/runwithspring.md new file mode 100644 index 000000000..292290319 --- /dev/null +++ b/docs/runwithspring.md @@ -0,0 +1,34 @@ +# 和spring进行集成 +如果你的项目中使用了spring,liteFlow可以很方便和spring进行集成 + +## 流程配置可以省略的部分 +流程配置中的`nodes`节点,可以不用配置了,支持spring的自动扫描方式。你需要在你的spring配置文件中定义 +```xml + + +``` + +当然,你的组件节点也需要注册进spirng容器 +```java +@Component("a") +public class AComponent extends NodeComponent + @Override + public void process() { + String str = this.getSlot().getRequestData(); + System.out.println(str); + System.out.println("Acomponent executed!"); + } +} +``` + +## spring中执行器的配置 +```xml + + + + /config/flow.xml + + + +``` +然后你的项目中通过spring拿到执行器进行调用流程。 \ No newline at end of file diff --git a/docs/runwithzookeeper.md b/docs/runwithzookeeper.md new file mode 100644 index 000000000..c364d63b1 --- /dev/null +++ b/docs/runwithzookeeper.md @@ -0,0 +1,20 @@ +## 和zookeeper进行集成 +liteFlow支持把配置放在zk集群中,并支持实时修改流程 + +# spring配置 +你只需在原来配置执行器的地方,把本地xml路径换成zk地址就ok了 +```xml + + + + + 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 + + + + + +``` + +如果你不加zkNode这个标签,就用默认的节点路径进行读取配置。 +使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1b490e37e..e09a9a044 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ liteflow jar 4.0.0 - 1.3.1 + 2.0.1 UTF-8 From 3237cd1e87f19bdee4f862182766fd756db7f0be Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:09:47 +0800 Subject: [PATCH 13/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.nojekyll | 0 docs/README.md | 74 +++++++++++++++++++++++++++++++++++++++++++ docs/architecture.md | 10 +++++- docs/guide.md | 1 + docs/runwithcustom.md | 6 ++-- 5 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 docs/.nojekyll create mode 100644 docs/README.md create mode 100644 docs/guide.md diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..effedd767 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,74 @@ +### 概述 +liteFlow是一个轻量,快速的组件式流程框架,帮助解耦业务代码,让每一个业务片段都是一个组件 + +* 提供本地xml的流程配置(后续全面支持spring式流程配置) +* 提供基于spring的扫描方式注入component +* 提供串行和并行2种模式。 +* 消除组件之间参数传递,引入数据总线概念。 +* 数据槽高并发隔离机制。 +* 提供无级嵌套条件节点的模式。 +* 自带简单的监控,能够知道每个组件的运行耗时排行(每隔5分钟会自动打印) + +### 最新版本1.3.1更新日志 +优化大量潜在的问题,此版本为稳定版本,主要更新点如下: +1. 增加条件节点功能 +2. 优化异常捕获的日志打印 +3. 支持自定义SLOT的特性 +4. 优化步骤打印,能够支持开闭区间的打印方式 +5. 增加了内部策略的调用方式 +6. 增加了追踪ID +7. 优化了监控打印 + +### Quick Start +1. 定义组件需继承Component,项目启动时会被自动发现。 +2. 定义xml配置(例子) +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` +3.spring里声明执行器 +```xml + + + + flow.xml + + + + + + + +``` +4.开始一个流程 +```java +executor.execute("chain2", 参数); +``` \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md index fa7cc9cb6..37f3711c4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1 +1,9 @@ -### \ No newline at end of file +## 架构设计 + +![architecture_image](images/architecture.png) + +组件式流程引擎架构设计 +Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) +EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU +Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 +Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽。 \ No newline at end of file diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 000000000..391250caa --- /dev/null +++ b/docs/guide.md @@ -0,0 +1 @@ +XXX \ No newline at end of file diff --git a/docs/runwithcustom.md b/docs/runwithcustom.md index 71963b000..df0411b10 100644 --- a/docs/runwithcustom.md +++ b/docs/runwithcustom.md @@ -1,7 +1,7 @@ -## 使用自定义的方式配置 -如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置的扩展点。 +## 使用自定义的配置源 +如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 -# 创建自定义的配置的类 +# 创建自定义配置源的类 在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 ```java public class TestCustomParser extends ClassXmlFlowParser { From fe044d91858acd4879f1e54ac42678840f6ed425 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:17:59 +0800 Subject: [PATCH 14/41] =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.nojekyll | 0 docs/README.md | 74 ---------------- docs/architecture.md | 9 -- docs/guide.md | 1 - docs/guide_cn.md | 180 +++++++++++++++++++++++++++++++++++++++ docs/quickstart.md | 62 -------------- docs/runwithcustom.md | 29 ------- docs/runwithspring.md | 34 -------- docs/runwithzookeeper.md | 20 ----- 9 files changed, 180 insertions(+), 229 deletions(-) delete mode 100644 docs/.nojekyll delete mode 100644 docs/README.md delete mode 100644 docs/architecture.md delete mode 100644 docs/guide.md create mode 100644 docs/guide_cn.md delete mode 100644 docs/quickstart.md delete mode 100644 docs/runwithcustom.md delete mode 100644 docs/runwithspring.md delete mode 100644 docs/runwithzookeeper.md diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index effedd767..000000000 --- a/docs/README.md +++ /dev/null @@ -1,74 +0,0 @@ -### 概述 -liteFlow是一个轻量,快速的组件式流程框架,帮助解耦业务代码,让每一个业务片段都是一个组件 - -* 提供本地xml的流程配置(后续全面支持spring式流程配置) -* 提供基于spring的扫描方式注入component -* 提供串行和并行2种模式。 -* 消除组件之间参数传递,引入数据总线概念。 -* 数据槽高并发隔离机制。 -* 提供无级嵌套条件节点的模式。 -* 自带简单的监控,能够知道每个组件的运行耗时排行(每隔5分钟会自动打印) - -### 最新版本1.3.1更新日志 -优化大量潜在的问题,此版本为稳定版本,主要更新点如下: -1. 增加条件节点功能 -2. 优化异常捕获的日志打印 -3. 支持自定义SLOT的特性 -4. 优化步骤打印,能够支持开闭区间的打印方式 -5. 增加了内部策略的调用方式 -6. 增加了追踪ID -7. 优化了监控打印 - -### Quick Start -1. 定义组件需继承Component,项目启动时会被自动发现。 -2. 定义xml配置(例子) -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` -3.spring里声明执行器 -```xml - - - - flow.xml - - - - - - - -``` -4.开始一个流程 -```java -executor.execute("chain2", 参数); -``` \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md deleted file mode 100644 index 37f3711c4..000000000 --- a/docs/architecture.md +++ /dev/null @@ -1,9 +0,0 @@ -## 架构设计 - -![architecture_image](images/architecture.png) - -组件式流程引擎架构设计 -Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) -EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU -Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 -Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽。 \ No newline at end of file diff --git a/docs/guide.md b/docs/guide.md deleted file mode 100644 index 391250caa..000000000 --- a/docs/guide.md +++ /dev/null @@ -1 +0,0 @@ -XXX \ No newline at end of file diff --git a/docs/guide_cn.md b/docs/guide_cn.md new file mode 100644 index 000000000..910af4f3d --- /dev/null +++ b/docs/guide_cn.md @@ -0,0 +1,180 @@ +# 一、快速开始 +liteflow需要你的项目使用maven +## 1.1依赖 +```xml + + com.thebeastshop.liteflow + liteflow + ${liteFlow.version} + +``` +## 1.2流程配置文件 +```xml + + + + + + + + + + + + + + + +``` + +component为组件,这里你需要实现这些组件,每个组件继承`NodeComponent`类 +```java +public class AComponent extends NodeComponent { + + @Override + public void process() { + String str = this.getSlot().getRequestData(); + System.out.println(str); + System.out.println("Acomponent executed!"); + } +} +``` + +chain为流程链,每个链上可配置多个组件节点。目前执行的模式分串行和并行2种。 +串行标签为`then`,并行标签为`when`。 +在串行的模式下,以下2种写法是等价的,可以根据业务需要来把不同种类的节点放一行里。 +```xml + +``` +```xml + + +``` + +## 1.3执行流程链 +```java +FlowExecutor executor = new FlowExecutor(); +executor.setRulePath(Arrays.asList(new String[]{"/config/flow.xml"})); +executor.init(); +Slot slot = executor.execute("demoChain", "arg"); +``` + +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/guide_cn?id=和spring进行集成) + +# 二、和spring进行集成 +如果你的项目中使用了spring,liteFlow可以很方便和spring进行集成 + +## 2.1流程配置可以省略的部分 +流程配置中的`nodes`节点,可以不用配置了,支持spring的自动扫描方式。你需要在你的spring配置文件中定义 +```xml + + +``` + +当然,你的组件节点也需要注册进spirng容器 +```java +@Component("a") +public class AComponent extends NodeComponent + @Override + public void process() { + String str = this.getSlot().getRequestData(); + System.out.println(str); + System.out.println("Acomponent executed!"); + } +} +``` + +## 2.2spring中执行器的配置 +```xml + + + + /config/flow.xml + + + +``` +然后你的项目中通过spring拿到执行器进行调用流程。 + +## 三、和zookeeper进行集成 +liteFlow支持把配置放在zk集群中,并支持实时修改流程 + +# 3.1spring配置 +你只需在原来配置执行器的地方,把本地xml路径换成zk地址就ok了 +```xml + + + + + 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 + + + + + +``` + +如果你不加zkNode这个标签,就用默认的节点路径进行读取配置。 +使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 + +## 四、使用自定义的配置源 +如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 + +# 4.1创建自定义配置源的类 +在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 +```java +public class TestCustomParser extends ClassXmlFlowParser { + + @Override + public String parseCustom() { + System.out.println("进入自定义parser"); + String xmlContent = null; + //这里需要自己扩展从自定义的地方获取配置 + return xmlContent; + } +} +``` + +# 4.2spring配置 +spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 +```xml + + + + com.thebeastshop.liteflow.test.TestCustomParser + + + +``` + +## 五、架构设计 + +![architecture_image](images/architecture.png) + +# 5.1组件式流程引擎架构设计 +Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) +EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU +Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 +Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽。 + +## 六、接入详细指南 +教你如何利用liteFlow写出低耦合,易扩展的项目 + +# 6.1执行器 +执行器`FlowExecutor`用来执行一个流程,用法为 +```java +public T execute(String chainId,Object param); +``` +第一个参数为流程ID,第二个参数为流程入参 +返回为`Slot`接口的子类,以上方法所返回的为默认的实现类`DefaultSlot` + +?> 实际在使用时,并不推荐用默认的`DefaultSlot`,推荐自己新建一个类继承`AbsSlot`类 +推荐使用带自定义Slot的执行接口: +```java +public T execute(String chainId,Object param,Class slotClazz); +``` + +关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/guide?id=数据槽) + +# 6.2数据槽 +数据槽 \ No newline at end of file diff --git a/docs/quickstart.md b/docs/quickstart.md deleted file mode 100644 index eab6d0a46..000000000 --- a/docs/quickstart.md +++ /dev/null @@ -1,62 +0,0 @@ -# 快速开始 -liteflow需要你的项目使用maven -## 依赖 -```xml - - com.thebeastshop.liteflow - liteflow - ${liteFlow.version} - -``` -## 流程配置文件 -```xml - - - - - - - - - - - - - - - -``` - -component为组件,这里你需要实现这些组件,每个组件继承`NodeComponent`类 -```java -public class AComponent extends NodeComponent { - - @Override - public void process() { - String str = this.getSlot().getRequestData(); - System.out.println(str); - System.out.println("Acomponent executed!"); - } -} -``` - -chain为流程链,每个链上可配置多个组件节点。目前执行的模式分串行和并行2种。 -串行标签为`then`,并行标签为`when`。 -在串行的模式下,以下2种写法是等价的,可以根据业务需要来把不同种类的节点放一行里。 -```xml - -``` -```xml - - -``` - -## 执行流程链 -```java -FlowExecutor executor = new FlowExecutor(); -executor.setRulePath(Arrays.asList(new String[]{"/config/flow.xml"})); -executor.init(); -Slot slot = executor.execute("demoChain", "arg"); -``` - -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/runwithspring) \ No newline at end of file diff --git a/docs/runwithcustom.md b/docs/runwithcustom.md deleted file mode 100644 index df0411b10..000000000 --- a/docs/runwithcustom.md +++ /dev/null @@ -1,29 +0,0 @@ -## 使用自定义的配置源 -如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 - -# 创建自定义配置源的类 -在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 -```java -public class TestCustomParser extends ClassXmlFlowParser { - - @Override - public String parseCustom() { - System.out.println("进入自定义parser"); - String xmlContent = null; - //这里需要自己扩展从自定义的地方获取配置 - return xmlContent; - } -} -``` - -# spring配置 -spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 -```xml - - - - com.thebeastshop.liteflow.test.TestCustomParser - - - -``` \ No newline at end of file diff --git a/docs/runwithspring.md b/docs/runwithspring.md deleted file mode 100644 index 292290319..000000000 --- a/docs/runwithspring.md +++ /dev/null @@ -1,34 +0,0 @@ -# 和spring进行集成 -如果你的项目中使用了spring,liteFlow可以很方便和spring进行集成 - -## 流程配置可以省略的部分 -流程配置中的`nodes`节点,可以不用配置了,支持spring的自动扫描方式。你需要在你的spring配置文件中定义 -```xml - - -``` - -当然,你的组件节点也需要注册进spirng容器 -```java -@Component("a") -public class AComponent extends NodeComponent - @Override - public void process() { - String str = this.getSlot().getRequestData(); - System.out.println(str); - System.out.println("Acomponent executed!"); - } -} -``` - -## spring中执行器的配置 -```xml - - - - /config/flow.xml - - - -``` -然后你的项目中通过spring拿到执行器进行调用流程。 \ No newline at end of file diff --git a/docs/runwithzookeeper.md b/docs/runwithzookeeper.md deleted file mode 100644 index c364d63b1..000000000 --- a/docs/runwithzookeeper.md +++ /dev/null @@ -1,20 +0,0 @@ -## 和zookeeper进行集成 -liteFlow支持把配置放在zk集群中,并支持实时修改流程 - -# spring配置 -你只需在原来配置执行器的地方,把本地xml路径换成zk地址就ok了 -```xml - - - - - 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 - - - - - -``` - -如果你不加zkNode这个标签,就用默认的节点路径进行读取配置。 -使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 \ No newline at end of file From f93ae35c4a1dcc1d02e3fa915615a99994002a41 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:22:52 +0800 Subject: [PATCH 15/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 910af4f3d..ebe0db987 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -97,9 +97,8 @@ public class AComponent extends NodeComponent 然后你的项目中通过spring拿到执行器进行调用流程。 ## 三、和zookeeper进行集成 -liteFlow支持把配置放在zk集群中,并支持实时修改流程 - # 3.1spring配置 +liteFlow支持把配置放在zk集群中,并支持实时修改流程 你只需在原来配置执行器的地方,把本地xml路径换成zk地址就ok了 ```xml @@ -118,9 +117,8 @@ liteFlow支持把配置放在zk集群中,并支持实时修改流程 使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 ## 四、使用自定义的配置源 -如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 - # 4.1创建自定义配置源的类 +如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 ```java public class TestCustomParser extends ClassXmlFlowParser { @@ -148,18 +146,14 @@ spring中需要改的地方还是执行器的配置,只需要在配置的路 ``` ## 五、架构设计 - -![architecture_image](images/architecture.png) - # 5.1组件式流程引擎架构设计 +![architecture_image](images/architecture.png) Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽。 ## 六、接入详细指南 -教你如何利用liteFlow写出低耦合,易扩展的项目 - # 6.1执行器 执行器`FlowExecutor`用来执行一个流程,用法为 ```java From 0f339c6e42abec549a36f85cc7756fa411b36166 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:25:05 +0800 Subject: [PATCH 16/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index ebe0db987..a04ab9942 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -62,8 +62,6 @@ Slot slot = executor.execute("demoChain", "arg"); 如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/guide_cn?id=和spring进行集成) # 二、和spring进行集成 -如果你的项目中使用了spring,liteFlow可以很方便和spring进行集成 - ## 2.1流程配置可以省略的部分 流程配置中的`nodes`节点,可以不用配置了,支持spring的自动扫描方式。你需要在你的spring配置文件中定义 ```xml From e6d331f9184e3ec18c8fadd097cccbd69bd5d296 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:31:26 +0800 Subject: [PATCH 17/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index a04ab9942..0f606f3d5 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -94,8 +94,8 @@ public class AComponent extends NodeComponent ``` 然后你的项目中通过spring拿到执行器进行调用流程。 -## 三、和zookeeper进行集成 -# 3.1spring配置 +# 三、和zookeeper进行集成 +## 3.1spring配置 liteFlow支持把配置放在zk集群中,并支持实时修改流程 你只需在原来配置执行器的地方,把本地xml路径换成zk地址就ok了 ```xml @@ -114,8 +114,8 @@ liteFlow支持把配置放在zk集群中,并支持实时修改流程 如果你不加zkNode这个标签,就用默认的节点路径进行读取配置。 使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 -## 四、使用自定义的配置源 -# 4.1创建自定义配置源的类 +# 四、使用自定义的配置源 +## 4.1创建自定义配置源的类 如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 ```java @@ -143,16 +143,16 @@ spring中需要改的地方还是执行器的配置,只需要在配置的路 ``` -## 五、架构设计 -# 5.1组件式流程引擎架构设计 +# 五、架构设计 +## 5.1组件式流程引擎架构设计 ![architecture_image](images/architecture.png) Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽。 -## 六、接入详细指南 -# 6.1执行器 +# 六、接入详细指南 +## 6.1执行器 执行器`FlowExecutor`用来执行一个流程,用法为 ```java public T execute(String chainId,Object param); @@ -168,5 +168,5 @@ public T execute(String chainId,Object param,Class Date: Wed, 28 Feb 2018 19:32:28 +0800 Subject: [PATCH 18/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 0f606f3d5..dd3ee4a7d 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -131,7 +131,7 @@ public class TestCustomParser extends ClassXmlFlowParser { } ``` -# 4.2spring配置 +## 4.2spring配置 spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 ```xml From e07a9350094b19b4007d6847486e601cb754c4a5 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:37:12 +0800 Subject: [PATCH 19/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index dd3ee4a7d..452fef591 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -131,7 +131,7 @@ public class TestCustomParser extends ClassXmlFlowParser { } ``` -## 4.2spring配置 +## 4.2Spring配置 spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 ```xml From e6ba11f7802b526eeadead5d956814eba054f912 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:39:17 +0800 Subject: [PATCH 20/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 452fef591..04ef2fc01 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -59,7 +59,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/guide_cn?id=和spring进行集成) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/?id=和spring进行集成) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -166,7 +166,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/guide?id=数据槽) +关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/?id=数据槽) ## 6.2数据槽 数据槽 \ No newline at end of file From 91f0f21e424b255597fa40db3d7b7eee0db9a04d Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 19:42:03 +0800 Subject: [PATCH 21/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 04ef2fc01..a2fb0b0cd 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -59,7 +59,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/?id=和spring进行集成) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/?id=二、和spring进行集成) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -166,7 +166,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/?id=数据槽) +关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/?id=6.2数据槽) ## 6.2数据槽 数据槽 \ No newline at end of file From 7ef0949344d0a03e5f5bc99ee67bc33eab649bd8 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 23:00:06 +0800 Subject: [PATCH 22/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index a2fb0b0cd..c2cbbbb82 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -40,9 +40,9 @@ public class AComponent extends NodeComponent { } ``` -chain为流程链,每个链上可配置多个组件节点。目前执行的模式分串行和并行2种。 -串行标签为`then`,并行标签为`when`。 -在串行的模式下,以下2种写法是等价的,可以根据业务需要来把不同种类的节点放一行里。 +chain为流程链,每个链上可配置多个组件节点。目前执行的模式分串行和并行2种。 +串行标签为`then`,并行标签为`when`。 +在串行的模式下,以下2种写法是等价的,可以根据业务需要来把不同种类的节点放一行里。 ```xml ``` @@ -96,7 +96,7 @@ public class AComponent extends NodeComponent # 三、和zookeeper进行集成 ## 3.1spring配置 -liteFlow支持把配置放在zk集群中,并支持实时修改流程 +liteFlow支持把配置放在zk集群中,并支持实时修改流程 你只需在原来配置执行器的地方,把本地xml路径换成zk地址就ok了 ```xml @@ -111,13 +111,13 @@ liteFlow支持把配置放在zk集群中,并支持实时修改流程 ``` -如果你不加zkNode这个标签,就用默认的节点路径进行读取配置。 -使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 +如果你不加zkNode这个标签,就用默认的节点路径进行读取配置。 +使用这种方式加载配置,在zk上进行更改配置。liteFlow会实时刷新配置。 # 四、使用自定义的配置源 ## 4.1创建自定义配置源的类 -如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 -在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 +如果你不想用本地的配置,也不打算使用zk作为配置持久化工具。liteFlow支持自定义的配置源的扩展点。 +在你的项目中创建一个类继承`ClassXmlFlowParser`这个类 ```java public class TestCustomParser extends ClassXmlFlowParser { @@ -132,7 +132,7 @@ public class TestCustomParser extends ClassXmlFlowParser { ``` ## 4.2Spring配置 -spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 +spring中需要改的地方还是执行器的配置,只需要在配置的路径地方放入自定义类的类路径即可 ```xml @@ -146,21 +146,21 @@ spring中需要改的地方还是执行器的配置,只需要在配置的路 # 五、架构设计 ## 5.1组件式流程引擎架构设计 ![architecture_image](images/architecture.png) -Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) -EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU -Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 -Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽。 +Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) +EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU +Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 +Data Bus:数据总线,用来存储整个调用链里数据。每一个请求生成一个数据槽。一个数据里最多有1024个数据槽 # 六、接入详细指南 ## 6.1执行器 -执行器`FlowExecutor`用来执行一个流程,用法为 +执行器`FlowExecutor`用来执行一个流程,用法为 ```java public T execute(String chainId,Object param); ``` -第一个参数为流程ID,第二个参数为流程入参 -返回为`Slot`接口的子类,以上方法所返回的为默认的实现类`DefaultSlot` +第一个参数为流程ID,第二个参数为流程入参 +返回为`Slot`接口的子类,以上方法所返回的为默认的实现类`DefaultSlot` -?> 实际在使用时,并不推荐用默认的`DefaultSlot`,推荐自己新建一个类继承`AbsSlot`类 +?> 实际在使用时,并不推荐用默认的`DefaultSlot`,推荐自己新建一个类继承`AbsSlot`类 推荐使用带自定义Slot的执行接口: ```java public T execute(String chainId,Object param,Class slotClazz); From a148dbaed2ea8233271850f2aed9feb4727ba554 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 23:06:10 +0800 Subject: [PATCH 23/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index c2cbbbb82..b5d5cf1e7 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -59,7 +59,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/?id=二、和spring进行集成) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -166,7 +166,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/?id=6.2数据槽) +关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) ## 6.2数据槽 数据槽 \ No newline at end of file From 5233e2604f74091c392dee213edab14e9d18d603 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 23:18:36 +0800 Subject: [PATCH 24/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index b5d5cf1e7..80f52f2bf 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -145,7 +145,7 @@ spring中需要改的地方还是执行器的配置,只需要在配置的路 # 五、架构设计 ## 5.1组件式流程引擎架构设计 -![architecture_image](images/architecture.png) +![architecture_image](https://github.com/thebeastshop/liteFlow/blob/master/docs/images/architecture.png) Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 From 7552193cd1a02ef8014cf96ae572de8b5cce928a Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 23:24:49 +0800 Subject: [PATCH 25/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 80f52f2bf..b52193dc6 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -145,7 +145,7 @@ spring中需要改的地方还是执行器的配置,只需要在配置的路 # 五、架构设计 ## 5.1组件式流程引擎架构设计 -![architecture_image](https://github.com/thebeastshop/liteFlow/blob/master/docs/images/architecture.png) +![architecture_image](https://raw.githubusercontent.com/thebeastshop/liteFlow/master/docs/images/architecture.png) Handler Unit:我们想象成每一个业务都是一个业务组件,每一个业务组件就是一个handlerUnit(处理单元) EPU:这里的epu对应的就是我们的执行器,用来统筹并处理handlerUnit。相当于计算机的CPU Event Bus:事件总线,用来指定下一个命令是什么,该如何去执行处理单元。这里的时间总线由我们的配置构成 From 331450e7cebbdbb0cf0fa1638a67d6cb7daa2286 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 28 Feb 2018 23:37:43 +0800 Subject: [PATCH 26/41] submit --- docs/guide_cn.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index b52193dc6..cb58d6a73 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -161,6 +161,9 @@ public T execute(String chainId,Object param); 返回为`Slot`接口的子类,以上方法所返回的为默认的实现类`DefaultSlot` ?> 实际在使用时,并不推荐用默认的`DefaultSlot`,推荐自己新建一个类继承`AbsSlot`类 +这是因为默认Slot实现类里面大多数都存放元数据,给用户扩展的数据存储是一个弱类型的Map +而用户自定义的Slot可以实现强类型的数据,这样对开发者更加友好 + 推荐使用带自定义Slot的执行接口: ```java public T execute(String chainId,Object param,Class slotClazz); From 29bd80a3c77246d367fb8f75367620c961c163f5 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Thu, 1 Mar 2018 00:25:12 +0800 Subject: [PATCH 27/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index cb58d6a73..47663a41b 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -172,4 +172,79 @@ public T execute(String chainId,Object param,Class 不过这里还是推荐扩展出自定义的Slot(上一小章阐述了原因),自定义的Slot更加友好。更加贴合业务。 + +## 6.3组件节点 +组件节点需要继承`NodeComponent`类 +需要实现`process`方法 +但是推荐实现`isAccess`方法,表示是否进入该节点,可以用于业务参数的预先判断 + +其他几个可以覆盖的方法有: +方法`isContinueOnError`:表示出错是否继续往下执行下一个组件 +方法`isEnd`:表示是否立即结束整个流程 + +在组件节点里,随时可以通过方法`getSlot`获取当前的数据槽,从而可以获取任何数据。 + +## 6.4条件节点 +在实际业务中,往往要通过动态的业务逻辑判断到底接下去该执行哪一个节点 +```xml + + + + +``` +利用表达式可以很方便的进行条件的判断 +c节点是用来路由的,被称为条件节点,这种节点需要继承`NodeCondComponent`类 +需要实现方法`processCond`,这个方法需要返回`Class`类型,就是具体的结果节点 + +## 6.5嵌套执行 +liteFlow可以无极嵌套执行n条流程 +```java +@Component("h") +public class HComponent extends NodeComponent { + + @Resource + private FlowExecutor flowExecutor; + + @Override + public void process() { + System.out.println("Hcomponent executed!"); + flowExecutor.invoke("strategy1",3, DefaultSlot.class, this.getSlotIndex()); + } + +} +``` +这段代码演示了在某个业务节点内调用另外一个流程链的方法 + +## 6.6步骤打印 +liteFlow在执行每一条流程链后会打印步骤 +样例如下: +``` +a==>c==>h(START)==>m==>p==>p1==>h(END)==>g +``` +?> 其中h节点分start和end两个步骤,说明在h节点内调用了另一条流程。start和end之间的步骤就是另一条流程的步骤 + +## 6.7监控 +liteFlow提供了简单的监控,目前只统计一个指标:每个组件的平均耗时 +每5分钟会打印一次,并且是根据耗时时长倒序排的。 + +# 七、更新记录 +## 1.3.1更新日志 +优化大量潜在的问题,此版本为稳定版本,主要更新点如下: +* 增加条件节点功能 +* 优化异常捕获的日志打印 +* 支持自定义SLOT的特性 +* 优化步骤打印,能够支持开闭区间的打印方式 +* 增加了内部策略的调用方式 +* 增加了追踪ID +* 优化了监控打印 + +## 2.0.1更新日志 +更新点如下: +* 增加对zookeeper的支持 +* 增加自定义配置源 +* 优化监控的表现 From 11ae42f291b1f10417479e622b3a39fcd8ad6c6d Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Thu, 1 Mar 2018 00:34:10 +0800 Subject: [PATCH 28/41] submit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 47663a41b..b2509b925 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -160,7 +160,7 @@ public T execute(String chainId,Object param); 第一个参数为流程ID,第二个参数为流程入参 返回为`Slot`接口的子类,以上方法所返回的为默认的实现类`DefaultSlot` -?> 实际在使用时,并不推荐用默认的`DefaultSlot`,推荐自己新建一个类继承`AbsSlot`类 +!> 实际在使用时,并不推荐用默认的`DefaultSlot`,推荐自己新建一个类继承`AbsSlot`类 这是因为默认Slot实现类里面大多数都存放元数据,给用户扩展的数据存储是一个弱类型的Map 而用户自定义的Slot可以实现强类型的数据,这样对开发者更加友好 @@ -176,7 +176,7 @@ public T execute(String chainId,Object param,Class 不过这里还是推荐扩展出自定义的Slot(上一小章阐述了原因),自定义的Slot更加友好。更加贴合业务。 +!> 不过这里还是推荐扩展出自定义的Slot(上一小章阐述了原因),自定义的Slot更加友好。更加贴合业务。 ## 6.3组件节点 组件节点需要继承`NodeComponent`类 From 93ac2f9b9179070c247700758b11bc412567a363 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Thu, 1 Mar 2018 10:45:36 +0800 Subject: [PATCH 29/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index b2509b925..d6609dd5a 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -59,7 +59,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144:3000/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -169,7 +169,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://123.206.92.144:3000/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) +关于`Slot`的说明,请参照[数据槽](http://123.206.92.144/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) ## 6.2数据槽 在执行器执行流程时会分配唯一的一个数据槽给这个请求。不同请求的数据槽是完全隔离的。 From bfc7c7f9bf838179451098de14f23e4ab5f1d0e4 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Thu, 1 Mar 2018 10:52:53 +0800 Subject: [PATCH 30/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 78 +++++++------------------------------------------------ 1 file changed, 10 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index f6ca617b3..34328f975 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,16 @@ -### 概述 -liteFlow是一个轻量,快速的组件式流程框架,帮助解耦业务代码,让每一个业务片段都是一个组件 +## 概述 +liteFlow是一个轻量,快速的组件式流程引擎框架,帮助解耦业务代码,让每一个业务片段都是一个组件,并支持热加载规则配置,实现即时修改。 -* 提供本地xml的流程配置(后续全面支持spring式流程配置) -* 提供基于spring的扫描方式注入component +[中文文档](http://123.206.92.144) + +## 特性 +* 提供本地xml的流程配置 +* 支持zookeeper流程配置,即时推送修改内容 +* 能自由扩展配置源,提供扩展接口 +* 和spring集成,支持spring的扫描方式 * 提供串行和并行2种模式。 * 消除组件之间参数传递,引入数据总线概念。 * 数据槽高并发隔离机制。 * 提供无级嵌套条件节点的模式。 -* 自带简单的监控,能够知道每个组件的运行耗时排行(每隔5分钟会自动打印) +* 自带简单的监控,能够知道每个组件的运行耗时排行 -### 最新版本1.3.1更新日志 -优化大量潜在的问题,此版本为稳定版本,主要更新点如下: -1. 增加条件节点功能 -2. 优化异常捕获的日志打印 -3. 支持自定义SLOT的特性 -4. 优化步骤打印,能够支持开闭区间的打印方式 -5. 增加了内部策略的调用方式 -6. 增加了追踪ID -7. 优化了监控打印 - -### Quick Start -1. 定义组件需继承Component,项目启动时会被自动发现。 -2. 定义xml配置(例子) -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` -3.spring里声明执行器 -```xml - - - - flow.xml - - - - - - - -``` -4.开始一个流程 -```java -executor.execute("chain2", 参数); -``` \ No newline at end of file From dd4771933f6ca29579d42d049235dbd0c059b9ac Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Fri, 2 Mar 2018 16:50:47 +0800 Subject: [PATCH 31/41] Update guide_cn.md --- docs/guide_cn.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index d6609dd5a..8479c005a 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -8,6 +8,8 @@ liteflow需要你的项目使用maven ${liteFlow.version} ``` +最新版本为2.0.1 +稳定版本为1.3.1 ## 1.2流程配置文件 ```xml From 2cf77dfd6b72451cb00568812ae030d5dfaddbcf Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Fri, 2 Mar 2018 16:53:05 +0800 Subject: [PATCH 32/41] Update guide_cn.md --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 8479c005a..d1bf3187c 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -8,8 +8,8 @@ liteflow需要你的项目使用maven ${liteFlow.version} ``` -最新版本为2.0.1 -稳定版本为1.3.1 +最新版本为**2.0.1**   +稳定版本为**1.3.1** ## 1.2流程配置文件 ```xml From ad8c2ddc7e55a7cd10defcb7f12a450aa2febdd7 Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Fri, 2 Mar 2018 16:55:01 +0800 Subject: [PATCH 33/41] Update guide_cn.md --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index d1bf3187c..bc80d5ab3 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -8,8 +8,8 @@ liteflow需要你的项目使用maven ${liteFlow.version} ``` -最新版本为**2.0.1**   -稳定版本为**1.3.1** +最新版本为**2.0.1**   +稳定版本为****1.3.1**** ## 1.2流程配置文件 ```xml From 98b4cd8b1960059b8e502efce4a131d6b6ef2a05 Mon Sep 17 00:00:00 2001 From: "everywhere.z" <47483522@qq.com> Date: Fri, 2 Mar 2018 16:55:20 +0800 Subject: [PATCH 34/41] Update guide_cn.md --- docs/guide_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index bc80d5ab3..d097be1ad 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -9,7 +9,7 @@ liteflow需要你的项目使用maven ``` 最新版本为**2.0.1**   -稳定版本为****1.3.1**** +稳定版本为**1.3.1** ## 1.2流程配置文件 ```xml From a6bebc78e9004eb0c73a99919810a431ff50b7dc Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Tue, 6 Mar 2018 14:37:04 +0800 Subject: [PATCH 35/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index d6609dd5a..e4cdbd454 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -232,7 +232,25 @@ a==>c==>h(START)==>m==>p==>p1==>h(END)==>g liteFlow提供了简单的监控,目前只统计一个指标:每个组件的平均耗时 每5分钟会打印一次,并且是根据耗时时长倒序排的。 -# 七、更新记录 +# 七、未来版本计划 +## 2.5版本 +* 支持更多的表达式,重写表达式解析器 +* 重新设计数据总线,解决数据槽热点问题 +* 增加一种驱动模式:消息驱动的模式 +* 对spring进行标签级支持 +* 对组件侵入更低,支持标注级声明 +* 增加监控的数据类型 + +## 2.6版本 +* 提供一个简单的组件注册中心 +* 有UI界面来查看监控数据 +* 此版本版本的重点功能:能用UI界面来回放整个执行过程(精确到数据槽里每一个对象) +* 此版本版本的重点功能:界面式设计规则 + +## 3.0版本 +主要是规则引擎的进化,制定规则文件。完善表达式引擎。 + +# 八、更新记录 ## 1.3.1更新日志 优化大量潜在的问题,此版本为稳定版本,主要更新点如下: * 增加条件节点功能 From 003c9320b1ba03cf4ba2b34cc5193dc023f266f2 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Tue, 6 Mar 2018 14:41:18 +0800 Subject: [PATCH 36/41] =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index d8f2307f1..4810c6c8f 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -246,8 +246,8 @@ liteFlow提供了简单的监控,目前只统计一个指标:每个组件的 ## 2.6版本 * 提供一个简单的组件注册中心 * 有UI界面来查看监控数据 -* 此版本版本的重点功能:能用UI界面来回放整个执行过程(精确到数据槽里每一个对象) -* 此版本版本的重点功能:界面式设计规则 +* 此版本的重点功能:能用UI界面来回放整个执行过程(精确到数据槽里每一个对象) +* 此版本的重点功能:界面式设计规则 ## 3.0版本 主要是规则引擎的进化,制定规则文件。完善表达式引擎。 From 50b073f56f0de847e949c7d9f6486174f2b5e197 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 2 Apr 2018 11:48:48 +0800 Subject: [PATCH 37/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 4810c6c8f..def955586 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -61,7 +61,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://yomahub.com/liteflow/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -171,7 +171,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://123.206.92.144/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) +关于`Slot`的说明,请参照[数据槽](http://yomahub.com/liteflow/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) ## 6.2数据槽 在执行器执行流程时会分配唯一的一个数据槽给这个请求。不同请求的数据槽是完全隔离的。 From caba1451b18ef2219fb9cc05c9e029d6b9194eaf Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Mon, 2 Apr 2018 13:37:31 +0800 Subject: [PATCH 38/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 34328f975..a7d196635 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 概述 liteFlow是一个轻量,快速的组件式流程引擎框架,帮助解耦业务代码,让每一个业务片段都是一个组件,并支持热加载规则配置,实现即时修改。 -[中文文档](http://123.206.92.144) +[中文文档](http://yomahub.com/liteflow) ## 特性 * 提供本地xml的流程配置 From 4189841c994d9e4711b25c0ea9a2267a741bbda4 Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 4 Apr 2018 15:36:39 +0800 Subject: [PATCH 39/41] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java b/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java index 151f951bd..46c1f3882 100644 --- a/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java +++ b/src/main/java/com/thebeastshop/liteflow/core/NodeComponent.java @@ -22,7 +22,6 @@ import com.thebeastshop.liteflow.entity.data.Slot; import com.thebeastshop.liteflow.entity.monitor.CompStatistics; import com.thebeastshop.liteflow.flow.FlowBus; import com.thebeastshop.liteflow.monitor.MonitorBus; -import com.thebeastshop.liteflow.parser.LocalXmlFlowParser; public abstract class NodeComponent { From e8f9cd1769354f9cd5b4d1ea8ef231cfc6ec9c1b Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Tue, 10 Apr 2018 10:28:56 +0800 Subject: [PATCH 40/41] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/guide_cn.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a7d196635..fbc0ba030 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 概述 liteFlow是一个轻量,快速的组件式流程引擎框架,帮助解耦业务代码,让每一个业务片段都是一个组件,并支持热加载规则配置,实现即时修改。 -[中文文档](http://yomahub.com/liteflow) +[中文文档](http://123.206.92.144/liteflow) ## 特性 * 提供本地xml的流程配置 diff --git a/docs/guide_cn.md b/docs/guide_cn.md index def955586..248523a0c 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -61,7 +61,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://yomahub.com/liteflow/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144/liteflow/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -171,7 +171,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://yomahub.com/liteflow/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) +关于`Slot`的说明,请参照[数据槽](http://123.206.92.144/liteflow/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) ## 6.2数据槽 在执行器执行流程时会分配唯一的一个数据槽给这个请求。不同请求的数据槽是完全隔离的。 From 8b7d192e9195781fc77e076841dfd995a002b53f Mon Sep 17 00:00:00 2001 From: "bryan.zhang" Date: Wed, 11 Apr 2018 14:47:13 +0800 Subject: [PATCH 41/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/guide_cn.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fbc0ba030..a7d196635 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 概述 liteFlow是一个轻量,快速的组件式流程引擎框架,帮助解耦业务代码,让每一个业务片段都是一个组件,并支持热加载规则配置,实现即时修改。 -[中文文档](http://123.206.92.144/liteflow) +[中文文档](http://yomahub.com/liteflow) ## 特性 * 提供本地xml的流程配置 diff --git a/docs/guide_cn.md b/docs/guide_cn.md index 248523a0c..def955586 100644 --- a/docs/guide_cn.md +++ b/docs/guide_cn.md @@ -61,7 +61,7 @@ executor.init(); Slot slot = executor.execute("demoChain", "arg"); ``` -如果你的项目使用spring,推荐参考[和Spring进行集成](http://123.206.92.144/liteflow/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) +如果你的项目使用spring,推荐参考[和Spring进行集成](http://yomahub.com/liteflow/#/?id=%e4%ba%8c%e3%80%81%e5%92%8cspring%e8%bf%9b%e8%a1%8c%e9%9b%86%e6%88%90) # 二、和spring进行集成 ## 2.1流程配置可以省略的部分 @@ -171,7 +171,7 @@ public T execute(String chainId,Object param); public T execute(String chainId,Object param,Class slotClazz); ``` -关于`Slot`的说明,请参照[数据槽](http://123.206.92.144/liteflow/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) +关于`Slot`的说明,请参照[数据槽](http://yomahub.com/liteflow/#/?id=_62%e6%95%b0%e6%8d%ae%e6%a7%bd) ## 6.2数据槽 在执行器执行流程时会分配唯一的一个数据槽给这个请求。不同请求的数据槽是完全隔离的。