Java项目之中间件:并发框架的学习、设计与实现

说明文档

项目名:TaskScheduler

参考资料:手写中间件之——并发框架

项目代码Gitee

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)中我们想要让最后的结果阻塞获取,或者想要设置阻塞的时间,可以使用如下方法:

方法参数返回值说明
getExecuteResultList<TaskResultOutput>阻塞返回
getExecuteResultlong timeout
TimeUnit unit
List<TaskResultOutput>阻塞返回,可设置超时时间
getExecuteResultOBTAIN_RESULT_MODE obtainModeList<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;
    }

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java微服务项目家居商城使用Redisson+RabbitMQ实现订单库存功能,大致的实现流程如下: 1. 当用户下单时,订单系统会将订单信息发送到消息队列中。 2. 库存系统订阅了该消息队列,在收到订单信息后,会从Redis中查询商品库存信息。 3. 如果库存充足,则将订单状态设置为已确认,并将库存数量减少;如果库存不足,则将订单状态设置为未确认。 4. 订单系统会定时轮询未确认的订单,并重新将其发送到消息队列中,直到库存充足为止。 该方案存在以下问题: 1. 并发性能问题:如果高并发下,多个订单同时发送到消息队列,库存系统处理每个订单的时间可能会很长,导致订单处理时间变长。 2. 可靠性问题:如果库存系统出现故障,订单系统无法及时得到库存状态的更新,可能会导致订单状态不准确。 3. 数据一致性问题:如果订单系统和库存系统使用的是不同的数据库,可能会出现数据不一致的问题。例如,订单系统已经将订单状态设置为已确认,但库存系统仍然未更新库存数量。 4. 重复消费问题:如果库存系统在处理订单时出现故障,可能会导致消息重复消费的问题,进而导致库存数量不准确。 针对这些问题,可以考虑以下解决方案: 1. 提高并发性能:可以通过增加库存系统的处理能力,或者将订单按照商品分类发送到不同的消息队列中来提高并发性能。 2. 提高可靠性:可以使用分布式事务框架来保证订单处理和库存更新的原子性,例如使用Seata等。 3. 确保数据一致性:可以使用相同的数据库,或者使用分布式事务框架来保证数据的一致性。 4. 避免重复消费:可以使用消息队列中间件提供的幂等性保证机制来避免重复消费的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值