前言

首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(2.问题与改进)]的支持。

分享一下近期我对这个项目的一些改进。

1. 增加了分支选择工程,可以根据节点的运行结果决定执行哪一个节点。

2. 增加了分布式运行节点功能,可以将流程节点部署到任意服务器,通过队列来调度节点,也就是说节点的运行将不在局限于Java语言。

1. 如何让流程图“Run”起来

首先我们回顾一下前两篇文章的知识,如何让流程图“Run”起来:

工程目录[ html/network.html ]里,提供了一个图形化界面的流程设计器,可以通过拖拽的方式设计流程并生成Json文件。

反之,也可以将Json文件转化为流程图并进行编辑。

1.1 创建流程

创建流程过程如下图所示:

节点和边的ID自动生成,可以自己定义节点名和每个边对应的节点返回值。

点击[ update json ]后,即可生成/更新流程图对应的Json文件。

1.2 节点与被执行的Java方法绑定

我们需要写一个Java类,继承自FlowRunner。

然后在里面写每个节点对应的方法,用@Node注释来实现与流程中节点的绑定。

同时将1.1中生成的Json文件放到和Java类相同的目录下。

TestFlow1.java

public class TestFlow1 extends FlowRunner {

	@Node(label = "a")
public int process_a() {
System.out.println("processing a");
return 1;
} @Node(label = "b")
public void process_b() {
System.out.println("processing b");
} @Node(label = "c")
public void process_c() {
System.out.println("processing c");
} @Node(label = "d")
public void process_d() {
System.out.println("processing d");
}
}

TestFlow1.json

{
"flowId": "your flow id",
"nodes": [
{
"id": "e21eb7b6-2f23-4264-a50f-e42321dd295b",
"label": "a",
"readyCheck": 0
},
{
"id": "f2a76819-b6a8-49db-af25-fab8274550f3",
"label": "b",
"readyCheck": 0
},
{
"id": "73f8bd68-8454-4b02-9098-c0c7bb6ffdb2",
"label": "c",
"readyCheck": 0
},
{
"id": "3553d1f7-e4c3-4e4b-a9ef-80b94ebbb8af",
"label": "d",
"readyCheck": 1
}
],
"edges": [
{
"id": "36bdc526-f6ae-45de-9bb7-34c293b34006",
"from": "e21eb7b6-2f23-4264-a50f-e42321dd295b",
"to": "f2a76819-b6a8-49db-af25-fab8274550f3",
"condition": "1",
"arrows": "to"
},
{
"id": "652b871d-338d-45f5-91a9-3a488ed9b6f4",
"from": "e21eb7b6-2f23-4264-a50f-e42321dd295b",
"to": "73f8bd68-8454-4b02-9098-c0c7bb6ffdb2",
"condition": "2",
"arrows": "to"
},
{
"id": "2691b6fe-ede9-4d1c-8b49-82d2a4ef014a",
"from": "f2a76819-b6a8-49db-af25-fab8274550f3",
"to": "3553d1f7-e4c3-4e4b-a9ef-80b94ebbb8af",
"arrows": "to"
},
{
"id": "d8026555-7609-4d27-8689-fd3dbcfe11d7",
"from": "73f8bd68-8454-4b02-9098-c0c7bb6ffdb2",
"to": "3553d1f7-e4c3-4e4b-a9ef-80b94ebbb8af",
"arrows": "to"
}
]
}

Test1.java

public class Test1 {

	public static void main(String[] args) {

		TestFlow1 testFlow = new TestFlow1();
testFlow.startFlow(true); }
}

1.3 启动流程

调用1.2中写好的Java类的startFlow方法,即可启动流程。

同步启动

TestFlow1 testFlow = new TestFlow1();
testFlow.startFlow(true);

异步启动

TestFlow1 testFlow = new TestFlow1();
testFlow.startFlow(false);

1.4 关闭流程执行器

流程执行器会在第一个流程启动的时候自动启动,在整个系统关闭的时候,我们需要将流程执行器关闭,如下。

FlowStarter.shutdown();

1.5 流程执行结果确认

在流程执行完毕后,日志会输出执行结果的json文件,我们可以将这个文件粘贴到1.1介绍的工具里,生成图形化的执行结果来确认节点的运行状况。

运行成功日志

Ready queue thread started.
Complete queue thread started.
json:
{"flowId":"123","nodes":[{"id":"1","label":"a"},{"id":"2","label":"b"},{"id":"0b5ba9df-b6c7-4752-94e2-debb6104015c","label":"c"},{"id":"29bc32c7-acd8-4893-9410-e9895da38b2e","label":"d"}],"edges":[{"id":"1","from":"1","to":"2","arrows":"to"},{"id":"078ffa82-5eff-4d33-974b-53890f2c9a18","from":"1","to":"0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows":"to"},{"id":"90663193-7077-4aca-9011-55bc8745403f","from":"2","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"},{"id":"a6882e25-c07a-4abd-907e-e269d4eda0ec","from":"0b5ba9df-b6c7-4752-94e2-debb6104015c","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"}]}
execute:1
node name:a
processing a
execute:2
node name:b
processing b
execute:0b5ba9df-b6c7-4752-94e2-debb6104015c
node name:c
processing c
execute:29bc32c7-acd8-4893-9410-e9895da38b2e
node name:d
processing d
Complete success.
json:
{"nodes":[{"id": "1","label": "a" ,"color": "#36AE7C"},{"id": "2","label": "b" ,"color": "#36AE7C"},{"id": "0b5ba9df-b6c7-4752-94e2-debb6104015c","label": "c" ,"color": "#36AE7C"},{"id": "29bc32c7-acd8-4893-9410-e9895da38b2e","label": "d" ,"color": "#36AE7C"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "078ffa82-5eff-4d33-974b-53890f2c9a18","from": "1","to": "0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows": "to"},{"id": "90663193-7077-4aca-9011-55bc8745403f","from": "2","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"},{"id": "a6882e25-c07a-4abd-907e-e269d4eda0ec","from": "0b5ba9df-b6c7-4752-94e2-debb6104015c","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"}]}

流程执行结束后,会输出执行结果和运行后的流程图状态。
可以直接将json贴到下面的位置,查看看结果(绿色表示正常结束,红色表示异常结束,白色表示等待执行)。

运行失败日志

Ready queue thread started.
Complete queue thread started.
json:
{"flowId":"123","nodes":[{"id":"1","label":"a"},{"id":"2","label":"b"},{"id":"0b5ba9df-b6c7-4752-94e2-debb6104015c","label":"c"},{"id":"29bc32c7-acd8-4893-9410-e9895da38b2e","label":"d"}],"edges":[{"id":"1","from":"1","to":"2","arrows":"to"},{"id":"078ffa82-5eff-4d33-974b-53890f2c9a18","from":"1","to":"0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows":"to"},{"id":"90663193-7077-4aca-9011-55bc8745403f","from":"2","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"},{"id":"a6882e25-c07a-4abd-907e-e269d4eda0ec","from":"0b5ba9df-b6c7-4752-94e2-debb6104015c","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"}]}
execute:1
node name:a
processing a
execute:2
node name:b
processing b
execute:0b5ba9df-b6c7-4752-94e2-debb6104015c
node name:c
processing c
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at io.github.nobuglady.network.fw.FlowRunner.execute(FlowRunner.java:49)
at io.github.nobuglady.network.fw.executor.NodeRunner.run(NodeRunner.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: test
at io.github.nobuglady.network.MyFlow1.process_b(MyFlow1.java:16)
... 11 more
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at io.github.nobuglady.network.fw.FlowRunner.execute(FlowRunner.java:49)
at io.github.nobuglady.network.fw.executor.NodeRunner.run(NodeRunner.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: test
at io.github.nobuglady.network.MyFlow1.process_b(MyFlow1.java:16)
... 11 more
Complete error.
json:
{"nodes":[{"id": "1","label": "a" ,"color": "#36AE7C"},{"id": "2","label": "b" ,"color": "#EB5353"},{"id": "0b5ba9df-b6c7-4752-94e2-debb6104015c","label": "c" ,"color": "#36AE7C"},{"id": "29bc32c7-acd8-4893-9410-e9895da38b2e","label": "d" ,"color": "#E8F9FD"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "078ffa82-5eff-4d33-974b-53890f2c9a18","from": "1","to": "0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows": "to"},{"id": "90663193-7077-4aca-9011-55bc8745403f","from": "2","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"},{"id": "a6882e25-c07a-4abd-907e-e269d4eda0ec","from": "0b5ba9df-b6c7-4752-94e2-debb6104015c","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"}]}

流程执行结束后,会输出执行结果和运行后的流程图状态。
可以直接将json贴到下面的位置,查看看结果(绿色表示正常结束,红色表示异常结束,白色表示等待执行)。

2. 分支选择的改进

2.1 分支选择定义

我们可以在图形界面中定义每条边的值,流程运行时,对节点的返回值与后续边的值进行比对,比对结果一直则执行该条边对应的后续节点。

2.2 节点启动条件定义

对于多条边Join到一个节点的情况,我们需要定义该节点启动的条件,如下:

1. 指向该节点的任意一条边通过检查,则启动该节点

2. 指想该节点的所有边都通过检车后,启动该节点

上图表示节点b和节点c 任意一个节点完成后,执行节点d

2.3 节点返回值绑定

节点的返回值与Java的方法返回值自动绑定,流程执行后,

对于有返回值的方法,则会调用该返回值的toString方法作为该节点的返回值。

对于无返回值的方法,则默认空文字列为返回值。

比如,返回int值,则用返回的int值与后续边的条件做对比。

@Node(label = "a")
public int process_a() {
System.out.println("processing a");
return 1;
}

返回String值,则用返回的String值与后续边的条件做对比。

@Node(label = "a")
public String process_a() {
System.out.println("processing a");
return "1";
}

返回自定义Object等,则用返回的Ojbect值的toString()方法生成的字符串与后续边的条件做对比。

	@Node(label = "a")
public MyObj process_a() {
System.out.println("processing a");
return new MyObj();
}

2.4 节点间参数传递

目前还没有对节点间参数传递做特别的处理,

可以通过类变量等方式进行节点间参数的传递。

3. 分布式运行的改进

3.1 系统结构

把单体的工程改进成分布式的工程,首先要明确系统结构和改进点。

目前的系统结构如下图所示(黄色部分可以配置成分布式运行)

系统通过两个队列来进行节点间控制信息的流转。

1. 待启动队列

2. 完成队列

3.1.1 待启动队列

生产者:流程管理器(FlowManager),流程启动后,流程管理器将初始节点放入[待启动队列]中,等待消费。

消费者:流程执行器(NodeExecutor),流程执行器监听[待启动队列],得到消息后,根据节点信息运行该节点,运行完成后,将节点的运行结果放入[完成队列]中,等待消费。

3.1.2 完成队列

生产者:流程执行器(NodeExecutor),流程执行器监听[待启动队列],得到消息后,根据节点信息运行该节点,运行完成后,将节点的运行结果放入[完成队列]中,等待消费。

消费者:流程管理器(FlowManager),流程管理器监听[完成队列],得到消息后,根据完成节点的信息,更新流程图,然后将后续待启动的节点放入[待启动队列]中,等待消费。

3.2 分布式系统改进

基于3.1介绍的系统结构,可以明显的发现队列是单机系统改进为分布式系统的改进点。

所以,把队列变成可配置的队列后,系统将可以通过配置文件选择单机部署,或者分布式部署。

配置文件如下

node.executor.remote=false
queue.ready.manager=io.github.nobuglady.network.fw.queue.ready.ReadyQueueManager
queue.complete.manager=io.github.nobuglady.network.fw.queue.complete.CompleteQueueManager
node.executor=io.github.nobuglady.network.fw.executor.NodePool

node.executor

对节点的执行器的配置,系统默认提供了本地的执行器,可以通过Annotation对节点绑定的方法进行调用。

您可以配置自己的节点执行器,需要实现接口INodeExecutor

onNodeReady:节点准备运行的时候,会调用这个方法

这个方法里需要写节点执行的具体方法,并且在节点执行完毕后,将节点运行结果放入[完成队列]。

shutdown:系统关闭的时候,会调用这个方法

node.executor.remote

false:本地执行

true:远程执行

本地执行时,会调用node.executor中配置的执行器,来执行节点的运行。

远程执行时,则系统不会启动 流程执行器(NodeExecutor)。也就是不会消费[待启动队列]中的消息。

远程执行时,目标系统监听[待启动队列]的消息,得到消息后,根据节点信息运行该节点,运行完成后,将节点的运行结果放入[完成队列]中,等待消费。

所以[待启动队列]和[完成队列]必须配置成远程系统可以访问的队列。

queue.ready.manager

待启动队列管理器

需要提供队列的消费和生产的方法

注:配置成远程执行节点时,系统不会调用此队列的消费方法。(由远程系统消费此队列信息)

queue.complete.manager

完成队列管理器

需要提供队列的消费和生产的方法

注:配置成远程执行节点时,系统不会调用此队列的生产方法。(由远程系统生产此队列信息)

5. 本地运行和分布式运行配置例

下面介绍以RabbiMQ作为远程队列,进行分布式调用的配置,选择其他的远程队列可以酌情修改。

工程里的test1-6分别对应如下6种启动方式,Test1-6.java为启动类。

每次启动之前,需要修改ladybugflow1-6.properties为ladybugflow.properties

1.  默认配置:通过流程类启动流程

启动代码

TestFlow1 testFlow = new TestFlow1();
testFlow.startFlow(true);
FlowStarter.shutdown();

2. 本地节点:自定义【待启动队列】和【完成队列】

启动代码

TestFlow2 testFlow = new TestFlow2();
testFlow.startFlow(true);
FlowStarter.shutdown();

3. 本地节点:自定义【节点执行器】

启动代码

TestFlow3 testFlow = new TestFlow3();
testFlow.startFlow(true);
FlowStarter.shutdown();

4. 远程节点:通过流程类启动流程

启动代码

TestFlow4 testFlow = new TestFlow4();
testFlow.startFlow(true);
FlowStarter.shutdown();

5. 默认配置:通过指定Json文件来启动流程

启动代码

FlowRunner flowRunner = new FlowRunner(new TestFlow5());
flowRunner.startFlowFromJson("io/github/nobuglady/network/demo/test5/TestFlow5.json", true);
FlowStarter.shutdown();

6. 远程节点:通过指定Json文件来启动流程

启动代码

FlowRunner flowRunner = new FlowRunner();
flowRunner.startFlowFromJson("io/github/nobuglady/network/demo/test5/TestFlow5.json", true);
FlowStarter.shutdown();

感谢您看文章读到这里。

最后

源码:https://github.com/nobuglady/ladybugflow

运行例源码:https://github.com/nobuglady/ladybugflow-demo

运行例源码(远程节点):https://github.com/nobuglady/ladybugflow-demo-remote-app

业务可视化-让你的流程图"Run"起来(3.分支选择&跨语言分布式运行节点)的更多相关文章

  1. 业务可视化-让你的流程图"Run"起来(4.实际业务场景测试)

    前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(3.分支选择&跨语言分布式运行节点)]的支持. 下面我以实际业务场景为例,来介绍一下ladybug ...

  2. 业务可视化-让你的流程图"Run"起来(2.问题与改进)

    前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来]的支持. 分享一下近期我对这个项目的一些改进. 问题&改进 问题1: 流程运行开始后,异步执行,无 ...

  3. 业务可视化-让你的流程图"Run"起来(6.定时任务&Spring-Batch的集成)

    前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(5.SpringBoot集成&微服务编排)]的支持. 分享一下近期我对这个项目的一些改进. 在项目 ...

  4. 业务可视化-让你的流程图"Run"起来

    前言 最近在研究业务可视化的问题,在日常的工作中,流程图和代码往往是分开管理的. 一个被维护多次的系统,到最后流程图和代码是否匹配这个都很难说. 于是一直有一个想法,让程序直接读流程图,根据流程图的配 ...

  5. 业务流程可视化-让你的流程图"Run"起来(7.运行状态持久化&轻量工作流支持)

    前言 感谢大家阅读本项目系列文章和对项目的支持.分享一下我对这个项目的新的改进. 之前项目做到了流程设计可视化和流程运行结果可视化. 本期发布的版本中实现了中间的运行过程的实时可视化,和流程状态持久化 ...

  6. npm run start失败&Node.js 查询指定端口运行情况及终止占用端口办法

    缘由: node.js项目中运行npm run start命令脚本报错,No such file or directory 最开始以为是命令脚本找不到所谓的执行路径,但后面发现不是,是package. ...

  7. Remix-Solidity IDE上run选项下Environment选择Web3 Provider,出现不能连接到测试网Ganache提示

    解决办法:通常情况下,自己使用的浏览器IDE是:https://ethereum.github.io/browser-solidity,如果出现连接不到Ganache测试网的提示,可以使用另一种浏览器 ...

  8. 新闻网大数据实时分析可视化系统项目——14、Spark2.X环境准备、编译部署及运行

    1.Spark概述 Spark 是一个用来实现快速而通用的集群计算的平台. 在速度方面, Spark 扩展了广泛使用的 MapReduce 计算模型,而且高效地支持更多计算模式,包括交互式查询和流处理 ...

  9. 数据可视化之powerBI入门(十一)认识Power BI数据分析语言DAX

    DAX是英文Data Analysis Expression的缩写,意思是数据分析表达式,从名称上就可以看出,DAX公式是用作数据分析的,事实上也确实如此,从数据分析层面认识DAX会更有助于我们理解它 ...

随机推荐

  1. 用 Docker 快速搭建 Kafka 集群

    开源Linux 一个执着于技术的公众号 版本 •JDK 14•Zookeeper•Kafka 安装 Zookeeper 和 Kafka Kafka 依赖 Zookeeper,所以我们需要在安装 Kaf ...

  2. 【DIY】【CSAPP-LAB】深入理解计算机系统--datalab笔记

    title: 前言 <深入理解计算机系统>一书是入门计算机系统的极好选择,从其第三版的豆瓣评分9.8分可见一斑.该书的起源是卡耐基梅龙大学 计算机系统入门课(Introduction to ...

  3. 使用grabit分析mysql数据库中的数据血缘关系

    使用grabit分析mysql数据库中的数据血缘关系 Grabit 是一个辅助工具,用于从数据库.GitHub 等修订系统.bitbucket 和文件系统等各种来源收集 SQL 脚本和存储过程,然后将 ...

  4. 【PyHacker编写指南】打造URL批量采集器

    这节课是巡安似海PyHacker编写指南的<打造URL批量采集器> 喜欢用Python写脚本的小伙伴可以跟着一起写一写呀. 编写环境:Python2.x 00x1: 需要用到的模块如下: ...

  5. kvm 虚拟化技术 1.1 安装

    1.·VMware开启虚拟化设置 2.安装一些虚拟化的必备插件 yum install -y qemu-kvm qemu-kvm-tools libvirt virt-manager virt-ins ...

  6. 让 API 测试变的简单。

    做开发已经四年有余了,之前在接口测试的时候最开始用的自己写的测试类进行测试,后来接触到了 postman 和 swagger ,虽然用起来比自己写的强太多了,但是总觉得差点事儿. 一方面是 postm ...

  7. Java高并发-多线程基础

    一.什么是线程 线程是进程内的执行单元. 二.线程的基本操作 2.1 状态与操作 2.2 新建线程 Thread t1 = new Thread(new CreateThread()); t1.sta ...

  8. linux篇-linux数据库mysql的安装

    1数据库文件放到opt下面 2赋予权限775 3运行脚本 4运行成功 5数据库操作 密码修改并刷新 权限修改,允许外部设备访问 6工具连接 7附录 1.显示当前数据库服务器中的数据库列表: mysql ...

  9. 微信小程序使用echarts遇到的问题

    问题1:ec-canvas出现上下滑动页面会漂移 解决方法:在标签内加 force-use-old-canvas="true" 问题2:echarts的tooltip会超出边界 解 ...

  10. CSP-J游记

    祝大家 CSP-J/CSP-S 稳过第一轮 ~(- ∨ -)~ ~~ 建议扩大110%食用 ~~ 中秋快乐鸭(希望大家不会收到损友送的砖头月饼 : − ) :-) :−)) 咳咳,昨天是我们可爱初赛来 ...