说明文档
项目名:TaskScheduler
参考资料:手写中间件之——并发框架
1、项目简介:
在微服务系统中,经常会有这样的调用场景:app(或web前端)调用后台的一个接口,该接口接到该请求后,需要调用其他多个微服务来获取数据,最终汇总一个最终结果返回给用户。
譬如用户请求“我的订单”,后台在收到请求后,就需要去调用用户详情 RPC、商品详情 RPC、库存 RPC、优惠券 RPC 等等很多个服务。有些服务是可以并行去请求的,但有些服务是依赖于某个服务的返回值的(如查库存、优惠券,就依赖于商品详情回复到达后才能去请求)。
TaskScheduler 就是这样一个用来解决任意的多线程并行、串行、阻塞、依赖、回调的并行框架。可以任意组合各线程的执行顺序,并且带有全链路执行结果回调。是多线程编排一站式解决方案。具体来说,
TaskScheduler 针对并发场景下可能存在的需求封装了具体的解决方案:
- 任意编排、执行顺序的强依赖和弱依赖
- 每个执行结果可以进行回调
- 依赖上游的执行结果作为入参
- 使用线程池保证低线程数
2、工作流式的任务编排
如何将整个流程进行编排并让其按照设定顺序执行,并能够合理处理异常情况,使一个并行框架所所要有的功能。
所以一个并行框架拥有的功能简单来说,至少具备下图的这种顺序编排能力。
3、使用方法
在TaskScheduler中,总体上分为执行图TaskGraph以及调度器Scheduler,而执行图又分为并发执行层TaskLayer,而并发层中包括执行节点TaskNode。
因此执行图TaskGraph是由一个个执行节点TaskNode所组成的,执行节点TaskNode是执行图TaskGraph的基本单位。
在执行节点TaskNode中包括执行任务Task以及执行任务ID、执行任务状态、执行任务所依赖的前置任务、执行参数、执行结果。
因此在使用中,需要先构造执行图TaskGraph,然后使用调度器Scheduler按照所构造好的执行图TaskGraph进行调度执行。
对于执行图TaskGraph中的执行节点TaskNode的定位分为ID绝对定位与序号相对定位。
如果不手动进行ID的设定,id为“当前层-当前层递增序号”,例如对于第0层的执行节点的话0-0,0-1,0-2,0-3…等等。同时同一层的节点也是有着相对顺序,其顺序从0开始递增,并且其顺序与添加节点的顺序有关,如果有必要的话也可以通过相对顺序进行执行节点的操作。
手动设定ID并不会删除ID,而是添加ID,因此以前的ID也是可以直接使用的。
一、简单的使用介绍
(1)、顺序执行
假设我们的输入为1,而A、B、C均将上一节点的值乘以2。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 构造执行图
*/
TaskGraph taskGraph = TaskNode.build(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, "A")
.nextTaskNode(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, "B")
.nextTaskNode(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, "C").end();
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
输出结果:
(2)、并行执行
对A、B、C同时输入1,并乘以2。
代码与(1)顺序执行并没有区别,区别只是将调用的函数nextTaskNode
换为了parallelTaskNode
。
.parallelTaskNode(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, "B")
输出结果:
(3)、A执行完后B、C再执行
对A同时输入1,并乘以2.然后将结果交给B、C再乘以2。
fork(MultiTaskMap MultiTaskMap, String... ids)
该函数可以将前一节点的输出参数共享给其分叉出来的子任务。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 构造执行图
*/
TaskGraph taskGraph = TaskNode.build(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, "A")
.fork(taskNodes -> {
taskNodes.get("B").plan(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
});
taskNodes.get("C").plan(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
});
}, "B", "C").end();
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
输出结果:
(4)、B、C都执行完成后或者B、C任意一个执行完成后执行A
对B、C同时输入1,并乘以2.然后将结果交给A累加。
先是测试B、C必须都执行完毕。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 构造执行图
*/
TaskGraph taskGraph = TaskNode.build(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("paramB", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("paramB"));
return properties;
}, "B")
.parallelTaskNode(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("paramC", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("paramC"));
return properties;
}, "C")
.collect(taskParams -> {
Properties properties = new Properties();
properties.put("param", (int) (taskParams.getTaskParam("B","paramB")) + (int) (taskParams.getTaskParam("C","paramC")) );
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, (the -> the.Id("A"))).end();
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
B、C必须都执行完毕,输出结果:
接下来测试B、C其中之一执行完毕,我们需要添加逻辑表达式在collect
函数中,并进行null
的判断
.collect(taskParams -> {
Properties properties = new Properties();
// 进行null的处理,直接字符串拼接
properties.put("param", (taskParams.getTaskParam("B","paramB")) +", "+ (taskParams.getTaskParam("C","paramC")) );
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, (the -> {the.Id("A");the.ExecuteMode("A | B");})).end(); // the.ExecuteMode(ExecuteMode.ANY)也是可以的
B、C必须任一执行完毕,输出结果:
在collect
函数中,逻辑表达式可以使用&、|、!分别表示与、或、非已经能够满足绝大部分需求。
(5)、如下图
对A输入1,并乘以2.然后将结果交给B、C乘以2,然后交给C、E乘以2、最后交给F累加。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 构造执行图
*/
TaskGraph taskGraph = TaskNode.build(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, "A")
.fork(taskNodes -> {
taskNodes.get("B").plan(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
});
taskNodes.get("C").plan(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
});
}, "B", "C")
.continueAddOneLayer(taskNodes -> {
taskNodes.get("D").plan(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
});
taskNodes.get("E").plan(taskParams -> {
Properties taskParam = taskParams.getTaskParams()[0];
Properties properties = new Properties();
properties.put("param", (int) taskParam.get("param") * 2);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
});
}, "D", "E")
.collect(taskParams -> {
int res = 0;
for (Properties taskParam : taskParams.getTaskParams()) {
res = res + (int)taskParam.get("param");
}
Properties properties = new Properties();
properties.put("param", res);
System.out.println(((TaskNode) taskParams).getId() + "->" + properties.get("param"));
return properties;
}, (the -> the.Id("F"))).end();
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
输出结果:
(6)、如下图
对A、D输入1,并乘以2.然后将结果交给B、C累加,E乘以2,然后交给F、G乘以2、最后交给H累加。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 如果觉得直接这样传入方法复杂的话,可以实现Task接口
*/
MyTask myTask = new MyTask(); // 乘以2
MyTask1 myTask1 = new MyTask1(); // 累加
/**
* 构造执行图
*/
TaskGraph taskGraph = TaskNode
.build(myTask, "A")
.fork(taskNodes -> {
taskNodes.get("B").plan(myTask);
taskNodes.get("C").plan(myTask);
}, "B", "C")
.collect(myTask1, (the -> the.Id("F")))
.end();
TaskGraph taskGraph1 = TaskNode.build(myTask, "D")
.nextTaskNode(myTask, "E")
.nextTaskNode(myTask, "G")
.end();
TaskGraph taskGraph2 = taskGraph.parallelTaskGraph(taskGraph1).endTaskNode()
.collect(myTask1, (the -> the.Id("H")))
.end();
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph2);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
输出结果:
(7)、更加复杂的情况
对于(6)中我们想要让B与C成为G的前驱节点,也就是B、C、E都完成后让G进行累和。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 如果觉得直接这样传入复杂的话,可以实现Task接口
*/
MyTask myTask = new MyTask(); // 乘以2
MyTask1 myTask1 = new MyTask1(); // 累加
/**
* 构造执行图
*/
TaskGraph taskGraph =
TaskNode.build(myTask, "A")
.fork(taskNodes -> {
taskNodes.get("B").plan(myTask);
taskNodes.get("C").plan(myTask);
}, "B", "C")
.collect(myTask1, (the -> the.Id("F")))
.end();
TaskGraph taskGraph1 = TaskNode.build(myTask, "D")
.nextTaskNode(myTask, "E")
.nextTaskNode(myTask1, "G")
.end();
TaskGraph taskGraph2 = taskGraph.parallelTaskGraph(taskGraph1).endTaskNode()
.collect(myTask1, (the -> the.Id("H")))
.end();
// 将B、C设置为G的前驱节点,并自定义G的收集模式,默认是前驱节点都完成了才执行
taskGraph2.addEdge("B", "G");
taskGraph2.addEdge("C", "G");
taskGraph2.setCollect("G", the -> {
the.ExecuteMode("B & C & E");
});
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph2);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
(8)阻塞或者设置阻塞时间获取结果
对于(7)中我们想要让最后的结果阻塞获取,或者想要设置阻塞的时间,可以使用如下方法:
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
getExecuteResult | 无 | List<TaskResultOutput> | 阻塞返回 |
getExecuteResult | long timeout TimeUnit unit | List<TaskResultOutput> | 阻塞返回,可设置超时时间 |
getExecuteResult | OBTAIN_RESULT_MODE obtainMode | List<TaskResultOutput> | 可选择阻塞或者立即返回 |
使用(7)中的列子进行结果的获取,阻塞10s,输出结果代码如下。
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
long currentTimeMillis = System.currentTimeMillis();
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph2);
/**
* 获取结果
*/
Thread.sleep(10);
System.out.println("-------------------------------------");
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS).get(0).getTaskResult();
System.out.println(executeResult.get("param"));
long endTimeMillis = System.currentTimeMillis();
System.out.println("-------------------------------------");
System.out.println("执行时间:"+(endTimeMillis - currentTimeMillis)/1000.0 + "(s)");
输出结果可看到了阻塞了大概10s:
二、事件机制
提供了事件机制对执行图中的重要时刻进行通知与监听,比如执行图初始化、执行节点初始化、执行节点参数设置、执行节点异常、执行节点执行完成、执行图执行完成等等预设的时间以及可以自定义事件。
在事件机制中,事件发布者有两个,分别是任务执行图TaskGraph以及任务执行节点TaskNode,事件监听器需要进行自定义并实现方法。
可以将事件监听器绑定在任务执行图TaskGraph以及任务执行节点TaskNode上面,如果想要监听每个任务执行节点的事件或者执行图的事件的话,
那么可以将事件监听器绑定在任务执行图TaskGraph上面。如果只是想要监听某个任务执行节点的话,那么绑定在任务执行节点上面。
预设的公共事件(任务执行图以及任务执行节点均可触发):
事件类 | 触发事件位置 |
---|---|
InitTaskNodeEvent | 任务执行节点初始化 |
AfterSetParamsTaskNodeEvent | 任务执行节点参数设置之后 |
AfterTaskErrorOnActionTaskNodeEvent | 任务执行节点发生错误之后 |
AfterTaskOnActionTaskNodeEvent | 任务节点执行之后(不管执行是否发生错误) |
AfterTaskSuccessOnActionTaskNodeEvent | 任务执行节点执行成功之后 |
预设的执行图事件(只有任务执行图可触发)
事件类 | 触发事件位置 |
---|---|
InitTaskGraphEvent | 任务图初始化 |
AfterTaskGraphHandleCompletedEvent | 任务图执行完毕 |
(1)、监听每个任务执行完后的结果
需要自定义事件监听器,并监听事件AfterTaskSuccessOnActionTaskNodeEvent
,然后绑定到任务执行图TaskGraph上面,将每个任务执行完成呼的结果打印出来。
自定义事件监听器PrintTaskResultListener
,实现打印结果的功能。
public class PrintTaskResultListener implements TaskEventListener<AfterTaskSuccessOnActionTaskNodeEvent> {
@Override
public void doEvent(TaskNode taskNode) {
System.out.println(taskNode.getId() + "的执行结果是:" + taskNode.getTaskResult("param"));
}
}
绑定事件监听器PrintTaskResultListener
到任务执行图上面。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 如果觉得直接这样传入复杂的话,可以实现Task接口
*/
MyTask myTask = new MyTask(); // 乘以2
MyTask1 myTask1 = new MyTask1(); // 累加
/**
* 构造执行图
*/
TaskGraph taskGraph =
TaskNode.build(myTask, "A")
.fork(taskNodes -> {
taskNodes.get("B").plan(myTask);
taskNodes.get("C").plan(myTask);
}, "B", "C")
.collect(myTask1, (the -> the.Id("F")))
.end();
TaskGraph taskGraph1 = TaskNode.build(myTask, "D")
.nextTaskNode(myTask, "E")
.nextTaskNode(myTask1, "G")
.end();
TaskGraph taskGraph2 = taskGraph.parallelTaskGraph(taskGraph1).endTaskNode()
.collect(myTask1, (the -> the.Id("H")))
.end();
taskGraph2.addEdge("B", "G");
taskGraph2.addEdge("C", "G");
taskGraph2.setCollect("G", the -> {
the.ExecuteMode("B & C & E");
});
// 绑定时间到任务执行图上
taskGraph2.boundListeners(new PrintTaskResultListener());
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph2);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
输出结果:
(2)、使用事件监听器完成异常处理
针对以下执行图。
F将会跑出异常,并抛出任务执行错误的事件AfterTaskErrorOnActionTaskNodeEvent
,我们自定义事件监听器处理该事件。
事件监听器监听到该异常事件后答应异常信息,并手动填充任务执行节点的输出结果为1000。
自定义异常处理事件监听器HandleExceptionListener
。
public class HandleExceptionListener implements TaskEventListener<AfterTaskErrorOnActionTaskNodeEvent> {
@Override
public void doEvent(TaskNode taskNode, Exception exception) {
System.err.println("异常信息:"+exception.getMessage());
Properties properties = new Properties();
properties.put("param", 1000);
taskNode.setTaskResult(properties);
}
}
自定义F执行节点的处理逻辑,直接抛出一个异常便可。
public class MyTask2 implements Task {
@Override
public Properties onAction(TaskParams taskParams) throws Exception {
throw new Exception("发生了异常");
}
}
主线程代码。
public class Main {
public static void main(String[] args) throws InterruptedException {
/**
* 准备初始参数
*/
TaskParams startTaskParams = new TaskParams();
startTaskParams.putTaskParam(0, "param", 1);
/**
* 如果觉得直接这样传入复杂的话,可以实现Task接口
*/
MyTask myTask = new MyTask(); // 乘以2
MyTask1 myTask1 = new MyTask1(); // 累加
MyTask2 myTask2 = new MyTask2(); // 抛出异常
/**
* 构造执行图
*/
TaskGraph taskGraph =
TaskNode.build(myTask, "A")
.fork(taskNodes -> {
taskNodes.get("B").plan(myTask);
taskNodes.get("C").plan(myTask);
}, "B", "C")
.collect(myTask2, (the -> the.Id("F"))) // 更改F执行节点抛出异常
.end();
TaskGraph taskGraph1 = TaskNode.build(myTask, "D")
.nextTaskNode(myTask, "E")
.nextTaskNode(myTask1, "G")
.end();
TaskGraph taskGraph2 = taskGraph.parallelTaskGraph(taskGraph1).endTaskNode()
.collect(myTask1, (the -> the.Id("H")))
.end();
taskGraph2.addEdge("B", "G");
taskGraph2.addEdge("C", "G");
taskGraph2.setCollect("G", the -> {
the.ExecuteMode("B & C & E");
});
// 绑定事件到任务执行图上
taskGraph2.boundListeners(new PrintTaskResultListener(), new HandleExceptionListener());
/**
* 创建调度器执行执行图,并获得最后输出的结果
*/
Scheduler execute = new Scheduler().execute(startTaskParams, taskGraph2);
Properties executeResult = execute.getExecuteResult(10, TimeUnit.SECONDS)[0];
System.out.println(executeResult.get("param"));
}
}
输出结果:
4、实现原理
构造一个执行图,将执行图大体抽象成为一层一层的节点,同一层之间节点并发执行,每个节点都拥有前置所依赖的节点,在执行的时都会判断所依赖的节点所依赖的节点是否完成。
执行图也是任务执行顺序的推进图,因为某些任务或许在协调执行整个任务中处于较后的位置,因此一开始并不会为其创建子线程加锁,而是随着任务执行的推进,前置任务使用完线程之后,可以通过线程池直接分配,这样大大降低了线程数。
在以上的执行图中每次只阻塞下一层的任务,而下一层之后的任务此时不会为其分配线程,因此这样可以大大降低线程数,提高资源的利用。
实现的关键代码,调度器Scheduler一层一层进行任务的推进。
public Scheduler execute(TaskParams startTaskParams, TaskGraph taskGraph){
// 任务执行图发布初始化事件
taskGraph.publicEvent(InitTaskGraphEvent.class, taskGraph);
// 任务执行图装载初始化代码
taskGraph.getStartTaskNodes().forEach(taskNode -> {
taskNode.loadTaskParam(startTaskParams);
});
endTaskNodes = taskGraph.getEndTaskNodes();
//遍历执行层,一层一层推进
for (TaskLayer taskLayer : taskGraph.getTaskLayers()) {
List<TaskNode> taskNodes = taskLayer.getTaskNodes();
for (TaskNode taskNode : taskNodes) {
// 触发任务执行节点初始化事件
taskNode.publicEvent(InitTaskNodeEvent.class, taskNode);
taskGraph.publicEvent(InitTaskNodeEvent.class, taskNode);
// 并发处理该层的任务
threadPoolExecutor.execute(() -> {
Properties result = null;
try {
// 阻塞该任务,通过其所依赖的节点的任务状态判断是否已经准备就绪
while ((!taskNode.isReady())){
if (taskNode.getLastTaskNodes().size() == 0){
break;
}
};
// 该任务已准备就绪,收集所依赖节点的输出参数
List<TaskNode> lastTaskNodes = taskNode.getLastTaskNodes();
taskNode.setTaskParamsNum(lastTaskNodes.size());
for (int i = 0; i < lastTaskNodes.size(); i++) {
Properties taskResult = lastTaskNodes.get(i).getTaskResult();
if (taskResult == null){
taskResult = new Properties();
}
taskNode.putTaskParams(i, taskResult);
taskNode.putTaskParams(lastTaskNodes.get(i).getId(), taskResult);
}
// 触发该任务参数设置之后事件
taskNode.publicEvent(AfterSetParamsTaskNodeEvent.class, taskNode);
taskGraph.publicEvent(AfterSetParamsTaskNodeEvent.class, taskNode);
// 执行该任务
result = taskNode.getCurTask().onAction(taskNode);
if (result == null){
result = new Properties();
}
// 保持该任务执行结果
taskNode.setTaskResult(result);
} catch (Exception e) {
// 触发该任务执行错误事件
taskNode.publicEvent(AfterTaskErrorOnActionTaskNodeEvent.class, taskNode, e);
taskGraph.publicEvent(AfterTaskErrorOnActionTaskNodeEvent.class, taskNode, e);
} finally {
// 触发该任务执行完成事件
taskNode.setTaskStatus(TaskStatus.COMPLETED);
taskNode.publicEvent(AfterTaskOnActionTaskNodeEvent.class, taskNode);
taskGraph.publicEvent(AfterTaskOnActionTaskNodeEvent.class, taskNode);
}
// 触发该任务执行成功事件
taskNode.publicEvent(AfterTaskSuccessOnActionTaskNodeEvent.class, taskNode);
taskGraph.publicEvent(AfterTaskSuccessOnActionTaskNodeEvent.class, taskNode);
if (taskNode.getTaskLayer().getLayer() == taskGraph.getTaskLayers().size() - 1){
// 设置整个任务执行图的执行结果
taskGraph.addTaskResultOutputs(taskNode);
// 触发该任务整个执行图执行完成事件
taskGraph.publicEvent(AfterTaskGraphHandleCompletedEvent.class, taskNode);
taskGraph.publicEvent(AfterTaskGraphHandleCompletedEvent.class, taskGraph);
}
});
}
}
return this;
}