并发编程大杀器,京东多线程编排工具asyncTool

news2024/11/25 14:48:04

图片

一、简介

并发编程大杀器,京东多线程编排工具asyncTool,可以解决任意的多线程并行、串行、阻塞、依赖、回调的并行框架,可以任意组合各线程的执行顺序,带全链路执行结果回调。多线程编排一站式解决方案。

二、特点

  • 多线程编排;

  • 全链路回调;

  • 超时控制;

解决任意的多线程并行、串行、阻塞、依赖、回调的并发框架,可以任意组合各线程的执行顺序,带全链路回调和超时控制。

其中的A、B、C分别是一个最小执行单元(worker),可以是一段耗时代码、一次Rpc调用等,不局限于你做什么。

该框架可以将这些worker,按照你想要的各种执行顺序,加以组合编排。最终得到结果。

并且,该框架 为每一个worker都提供了执行结果的回调和执行失败后自定义默认值 。譬如A执行完毕后,A的监听器会收到回调,带着A的执行结果(成功、超时、异常)。

根据你的需求,将各个执行单元组合完毕后,开始在主线程执行并阻塞,直到最后一个执行完毕。并且 可以设置全组的超时时间 。

该框架支持后面的执行单元以前面的执行单元的结果为自己的入参 。譬如你的执行单元B的入参是ResultA,ResultA就是A的执行结果,那也可以支持。在编排时,就可以预先设定B或C的入参为A的result,即便此时A尚未开始执行。当A执行完毕后,自然会把结果传递到B的入参去。

三、并行场景

1. 客户端请求服务端接口,该接口需要调用其他N个微服务的接口

譬如 请求我的购物车,那么就需要去调用用户的rpc、商品详情的rpc、库存rpc、优惠券等等好多个服务。同时,这些服务还有相互依赖关系,譬如必须先拿到商品id后,才能去库存rpc服务请求库存信息。最终全部获取完毕后,或超时了,就汇总结果,返回给客户端。

2. 并行执行N个任务,后续根据这1-N个任务的执行结果来决定是否继续执行下一个任务

如用户可以通过邮箱、手机号、用户名登录,登录接口只有一个,那么当用户发起登录请求后,我们需要并行根据邮箱、手机号、用户名来同时查数据库,只要有一个成功了,都算成功,就可以继续执行下一步。而不是先试邮箱能否成功、再试手机号……

再如某接口限制了每个批次的传参数量,每次最多查询10个商品的信息,我有45个商品需要查询,就可以分5堆并行去查询,后续就是统计这5堆的查询结果。就看你是否强制要求全部查成功,还是不管有几堆查成功都给客户做返回

再如某个接口,有5个前置任务需要处理。其中有3个是必须要执行完毕才能执行后续的,另外2个是非强制的,只要这3个执行完就可以进行下一步,到时另外2个如果成功了就有值,如果还没执行完,就是默认值。

3. 需要进行线程隔离的多批次任务

如多组任务, 各组任务之间彼此不相关,每组都需要一个独立的线程池,每组都是独立的一套执行单元的组合。有点类似于hystrix的线程池隔离策略。

4. 单机工作流任务编排

5. 其他有顺序编排的需求

四、应用需求

1. 并行场景可能存在的需求之——每个执行结果的回调

传统的Future、CompleteableFuture一定程度上可以完成任务编排,并可以把结果传递到下一个任务。如CompletableFuture有then方法,但是却无法做到对每一个执行单元的回调。譬如A执行完毕成功了,后面是B,我希望A在执行完后就有个回调结果,方便我监控当前的执行状况,或者打个日志什么的。失败了,我也可以记录个异常信息什么的。

此时,CompleteableFuture就无能为力了。

我的框架提供了这样的回调功能。并且,如果执行异常、超时,可以在定义这个执行单元时就设定默认值。

2. 并行场景可能存在的需求之——执行顺序的强依赖和弱依赖

有些场景下,我们希望B和C都执行完毕后,才能执行A,CompletableFuture里有个allOf(futures...).then()方法可以做到。

有些场景下,我们希望B或者C任何一个执行完毕,就执行A,CompletableFuture里有个anyOf(futures...).then()方法可以做到。

我的框架同样提供了类似的功能,通过设定wrapper里的addDepend依赖时,可以指定依赖的任务是否must执行完毕。如果依赖的是must要执行的,那么就一定会等待所有的must依赖项全执行完毕,才执行自己。

如果依赖的都不是must,那么就可以任意一个依赖项执行完毕,就可以执行自己了。

注意:这个依赖关系是有必须和非必须之分的,还有一个重要的东西是执行单元不能重复执行。譬如图4,如果B执行完毕,然后执行了A,此时C终于执行完了,然后也到了A,此时就会发现A已经在执行,或者已经完毕(失败),那么就不应该再重复执行A。

还有一种场景,如下图,A和D并行开始,D先执行完了,开始执行Result任务,此时B和C都还没开始,然后Result执行完了,虽然B和C都还没执行,但是已经没必要执行了。B和C这些任务是可以被跳过的,跳过的原则是他们的NextWrapper已经有结果了或者已经在执行了。我提供了checkNextWrapperResult方法来控制,当后面的任务已经执行了,自己还要不要执行的逻辑控制。当然,这个控制仅限于nextWrapper只有一个时才成立。

3. 并发场景可能存在的需求之——依赖上游的执行结果作为入参

譬如A-B-C三个执行单元,A的入参是String,出参是int,B呢它需要用A的结果作为自己的入参。也就是说A、B并不是独立的,而是有结果依赖关系的。

在A执行完毕之前,B是取不到结果的,只是知道A的结果类型。

那么,我的框架也支持这样的场景。可以在编排时,就取A的结果包装类,作为B的入参。虽然此时尚未执行,必然是空,但可以保证A执行完毕后,B的入参会被赋值。

在V1.3后,框架支持在worker的action的入参Map<String, WorkerWrapper>中获取任意一个执行单元的执行结果,当然,可以取其中的1个、多个执行结果作为自己的入参。Key就是在定义wrapper时通过id传进来的唯一id标识。详情demo可以查看test包下dependnew包案例。

4. 并发场景可能存在的需求之——全组任务的超时

一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。

5. 并发场景可能存在的需求之——高性能、低线程数

该框架全程无锁,不依靠线程锁来保证顺序。

五、基本组件

1. worker

一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。

T,V两个泛型,分别是入参和出参类型。

譬如该耗时操作,入参是String,执行完毕的结果是Integer,那么就可以用泛型来定义。

多个不同的worker之间,没有关联,分别可以有不同的入参、出参类型。


/**
 * 每个最小执行单元需要实现该接口
 * @author wuweifeng wrote on 2019-11-19.
 */
public interface IWorker<T, V> {
    /**
     * 在这里做耗时操作,如rpc请求、IO等
     *
     * @param object
     *         object
     */
    V action(T object, Map<String, WorkerWrapper> allWrappers);

    /**
     * 超时、异常时,返回的默认值
     * @return 默认值
     */
    V defaultValue();
}

2. callBack

对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。


/**
 * 每个执行单元执行完毕后,会回调该接口</p>
 * 需要监听执行结果的,实现该接口即可
 * @author wuweifeng wrote on 2019-11-19.
 */
public interface ICallback<T, V> {

    void begin();

    /**
     * 耗时操作执行完毕后,就给value注入值
     *
     */
    void result(boolean success, T param, WorkResult<V> workResult);
}

3. wrapper

组合了worker和callback,是一个 最小的调度单元 。通过编排wrapper之间的关系,达到组合各个worker顺序的目的。

wrapper的泛型和worker的一样,决定了入参和结果的类型。

WorkerWrapper<String, String> workerWrapper = new WorkerWrapper<>(w, "0", w);
WorkerWrapper<String, String> workerWrapper1 = new WorkerWrapper<>(w1, "1", w1);
WorkerWrapper<String, String> workerWrapper2 = new WorkerWrapper<>(w2, "2", w2);
WorkerWrapper<String, String> workerWrapper3 = new WorkerWrapper<>(w3, "3", w3);

图片

0执行完,同时1和2, 1\2都完成后3。3会等待2完成

此时,你可以定义一个 worker


/**
 * @author wuweifeng wrote on 2019-11-20.
 */
public class ParWorker1 implements IWorker<String, String>, ICallback<String, String> {

    @Override
    public String action(String object) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "result = " + SystemClock.now() + "---param = " + object + " from 1";
    }

    @Override
    public String defaultValue() {
        return "worker1--default";
    }

    @Override
    public void begin() {
        //System.out.println(Thread.currentThread().getName() + "- start --" + System.currentTimeMillis());
    }

    @Override
    public void result(boolean success, String param, WorkResult<String> workResult) {
        if (success) {
            System.out.println("callback worker1 success--" + SystemClock.now() + "----" + workResult.getResult()
                    + "-threadName:" +Thread.currentThread().getName());
        } else {
            System.err.println("callback worker1 failure--" + SystemClock.now() + "----"  + workResult.getResult()
                    + "-threadName:" +Thread.currentThread().getName());
        }
    }

}

通过这一个类看一下,action里就是你的耗时操作,begin就是任务开始执行时的回调,result就是worker执行完毕后的回调。当你组合了多个执行单元时,每一步的执行,都在掌控之内。失败了,还会有自定义的默认值。这是CompleteableFuture无法做到的。

六、项目引入

外网使用jitpack.io上打的包 先添加repositories节点

  <repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
  </repositories>

然后添加如下maven依赖

  <dependency>
      <groupId>com.gitee.jd-platform-opensource</groupId>
      <artifactId>asyncTool</artifactId>
      <version>V1.4-SNAPSHOT</version>
  </dependency>

七、使用说明

1. 三个任务并行

图片

        ParWorker w = new ParWorker();
        ParWorker1 w1 = new ParWorker1();
        ParWorker2 w2 = new ParWorker2();

        WorkerWrapper<String, String> workerWrapper2 =  new WorkerWrapper.Builder<String, String>()
                .worker(w2)
                .callback(w2)
                .param("2")
                .build();

        WorkerWrapper<String, String> workerWrapper1 =  new WorkerWrapper.Builder<String, String>()
                .worker(w1)
                .callback(w1)
                .param("1")
                .build();

        WorkerWrapper<String, String> workerWrapper =  new WorkerWrapper.Builder<String, String>()
                .worker(w)
                .callback(w)
                .param("0")
                .build();

        long now = SystemClock.now();
        System.out.println("begin-" + now);

        Async.beginWork(1500, workerWrapper, workerWrapper1, workerWrapper2);
//        Async.beginWork(800, workerWrapper, workerWrapper1, workerWrapper2);
//        Async.beginWork(1000, workerWrapper, workerWrapper1, workerWrapper2);

        System.out.println("end-" + SystemClock.now());
        System.err.println("cost-" + (SystemClock.now() - now));
        System.out.println(Async.getThreadCount());

        System.out.println(workerWrapper.getWorkResult());
        Async.shutDown();
       

2. 一个执行完毕后,开启另外两个,另外两个执行完毕后,开始第四个

图片

        ParWorker w = new ParWorker();
        ParWorker1 w1 = new ParWorker1();
        ParWorker2 w2 = new ParWorker2();
        ParWorker3 w3 = new ParWorker3();

        WorkerWrapper<String, String> workerWrapper3 =  new WorkerWrapper.Builder<String, String>()
                .worker(w3)
                .callback(w3)
                .param("3")
                .build();

        WorkerWrapper<String, String> workerWrapper2 =  new WorkerWrapper.Builder<String, String>()
                .worker(w2)
                .callback(w2)
                .param("2")
                .next(workerWrapper3)
                .build();

        WorkerWrapper<String, String> workerWrapper1 =  new WorkerWrapper.Builder<String, String>()
                .worker(w1)
                .callback(w1)
                .param("1")
                .next(workerWrapper3)
                .build();

        WorkerWrapper<String, String> workerWrapper =  new WorkerWrapper.Builder<String, String>()
                .worker(w)
                .callback(w)
                .param("0")
                .next(workerWrapper1, workerWrapper2)
                .build();


        long now = SystemClock.now();
        System.out.println("begin-" + now);

        Async.beginWork(3100, workerWrapper);
//        Async.beginWork(2100, workerWrapper);

        System.out.println("end-" + SystemClock.now());
        System.err.println("cost-" + (SystemClock.now() - now));

        System.out.println(Async.getThreadCount());
        Async.shutDown();

如果觉得这样不符合左右的顺序,也可以用这种方式:

        WorkerWrapper<String, String> workerWrapper =  new WorkerWrapper.Builder<String, String>()
                .worker(w)
                .callback(w)
                .param("0")
                .build();

        WorkerWrapper<String, String> workerWrapper3 =  new WorkerWrapper.Builder<String, String>()
                .worker(w3)
                .callback(w3)
                .param("3")
                .build();

        WorkerWrapper<String, String> workerWrapper2 =  new WorkerWrapper.Builder<String, String>()
                .worker(w2)
                .callback(w2)
                .param("2")
                .depend(workerWrapper)
                .next(workerWrapper3)
                .build();

        WorkerWrapper<String, String> workerWrapper1 =  new WorkerWrapper.Builder<String, String>()
                .worker(w1)
                .callback(w1)
                .param("1")
                .depend(workerWrapper)
                .next(workerWrapper3)
                .build();

3. 依赖别的worker执行结果作为入参

可以从action的参数中根据wrapper的id获取任意一个执行单元的执行结果,但请注意执行顺序,如果尚未执行,则在调用WorkerResult.getResult()会得到null!

图片

图片

其他的详见test包下的测试类,支持各种形式的组合、编排。

gitee地址:

https://gitee.com/jd-platform-opensource/asyncTool

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1343123.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构学习 Leetcode72 编辑距离

关键词&#xff1a;动态规划 最长公共子序列 题目&#xff1a; 思路&#xff1a; 这题我虽然做出来了但是还是有点迷糊。首先&#xff0c;这道题一定是和最长公共子序列相似的。 所以往最长公共子序列方向思考&#xff0c;考虑的它的状态和转移方程以及边界。 状态和转移方…

PAT乙级 1025 反转链表

给定一个常数 K 以及一个单链表 L&#xff0c;请编写程序将 L 中每 K 个结点反转。例如&#xff1a;给定 L 为 1→2→3→4→5→6&#xff0c;K 为 3&#xff0c;则输出应该为 3→2→1→6→5→4&#xff1b;如果 K 为 4&#xff0c;则输出应该为 4→3→2→1→5→6&#xff0c;即…

web自动化上传文件

1&#xff0c;web 自动化文件上传不要太简单 熟悉 web 自动化测试的大佬应该都懂&#xff0c;当采用 js 调用原生控件进行文件上传的时候&#xff0c;最常用的是使用 pywin32 等系统交互库。 当看到 pywin32 那丑陋的 api 封装只能爆粗口。就为了输入一个文件地址&#xff0c;…

微生信 -- 0代码科研绘图,助力发高分文章

1&#xff0c;粘贴数据&#xff0c;一键出图 www.bioinformatics.com.cn微生信云平台以220多款在线绘图、分析模块为基础&#xff0c;致力于0代码在线分析数据&#xff0c;0代码在线绘制科研图片。让不会编写代码的科研工作者&#xff0c;贴贴数据&#xff0c;点点鼠标就能够进…

大模型中的LM-BFF

LM-BFF paper: 2020.12 Making Pre-trained Language Models Better Few-shot Learners Prompt: 完形填空自动搜索prompt Task: Text Classification Model: Bert or Roberta Take Away: 把人工构建prompt模板和标签词优化为自动搜索 LM-BFF是陈丹琦团队在20年底提出的针对…

基于yolov2深度学习网络的血细胞检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1YOLOv2算法原理 4.2 YOLOv2网络结构 4.3 血细胞检测算法实现 数据集准备 数据预处理 网络训练 模型评估与优化 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MAT…

Unity中Shader裁剪空间推导(透视相机到裁剪空间的转化矩阵)

文章目录 前言一、简单看一下 观察空间—>裁剪空间—>屏幕空间 的转化1、观察空间&#xff08;右手坐标系、透视相机&#xff09;2、裁剪空间&#xff08;左手坐标系、且转化为了齐次坐标&#xff09;3、屏幕空间&#xff08;把裁剪坐标归一化设置&#xff09;4、从观察空…

Java学习——设计模式——结构型模式1

文章目录 结构型模式代理模式适配器模式 结构型模式 结构型模式主要涉及如何组合各种对象以便获得更好、更灵活的结构。虽然面向对象的继承机制提供了最基本的子类扩展父类的功能&#xff0c;但结构型模式不仅仅简单地使用继承&#xff0c;而更多地通过组合与运行期的动态组合来…

开关量信号隔离器在水泥厂的应用

安科瑞 崔丽洁 摘要&#xff1a;在水泥厂生产过程中&#xff0c;需要皮带与提升机进行运输物料&#xff0c;回转下料器进行投放物料。其中涉及大量电动机相关的机械运动&#xff0c;对这些机械运动状态进行检测&#xff0c;可以及时发现问题避免造成事故。针对机械位置检测&…

前端图片适配不同屏幕方案

预备知识&#xff1a; 设备独立像素,以下图的iphone12 Pro为例&#xff0c;390*844表示的就是设备独立像素&#xff08;DIP&#xff09;,也可以理解为CSS像素 物理像素&#xff08;设备像素&#xff09;&#xff0c;就是屏幕的分辨率&#xff0c;显示屏就是由一个个物理像素…

基于EMD的滚动轴承故障诊断算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1EMD的基本原理 固有模态函数&#xff08;IMF&#xff09; 筛分过程 4.2 基于EMD的滚动轴承故障诊断算法 信号预处理 EMD分解 特征提取 故障诊断 5.算法完整程序工程 1.算法运行效…

Python爱心光波完整代码

文章目录 环境需求完整代码详细分析环境需求 python3.11.4PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.blog.csdn.net/arti…

3d导入模型怎样显示原本材质---模大狮模型网

要在导入3D模型时保留原本的材质&#xff0c;您可以尝试以下方法&#xff1a; 导入前检查文件格式&#xff1a;确保您所使用的3D软件支持导入模型的文件格式。不同的软件对文件格式的支持有所差异&#xff0c;选择正确的文件格式可以更好地保留原始材质。 使用正确的材质库&am…

pip freeze 导出的requirements.txt文件含有 @file

使用 pip freeze > requirements.txt 导出的文件中含有 file&#xff1a;// 使用这个文件在其他环境下安装就会出现 找不到路径的错误 原因是这种方法生成的文件只能用于本地环境&#xff0c;无法供他们使用 使用以下方式生成文件 解决&#xff1a;pip list --formatfreeze …

【数据库系统概论】第6章-关系数据库理论

真别看吧&#xff0c;抄ppt而已啊 文章目录 6.1 引言6.2 规范化6.2.1 函数依赖6.2.2 码6.2.3 范式&#xff08;Normal Form&#xff09;6.2.4 BC范式6.2.5 规范化小结 6.1 引言 我们有这样一张表&#xff1a; but 为啥这样设计呢&#xff1f;由此引出怎样设计一个关系数据库…

C++的多继承和虚继承

目录 多继承的定义和用法定义多继承多继承中派生类对象的内存布局访问基类成员多继承带来的问题 虚继承虚继承的语法虚继承对象的内存布局虚继承中的构造虚继承的缺点 多继承的定义和用法 C支持多继承&#xff0c;即一个派生类可以有多个基类。 很多时候&#xff0c;单继承就…

Spring boot:3.X + Security OAuth2 自定义登录页面、登出后跳转到登录页

本文描述了基于 Spring Oauth2 的 code 模式&#xff0c;实现登陆同时授权、自定义登录界面、登出页面的功能。 1.Maven依赖&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactI…

文件打包上传linux服务器,通过http协议url下载

背景&#xff1a;服务器部署tomcat&#xff0c;已经运行项目 直接在编译后的class文件新建文件&#xff0c;把想要下载的文件放入&#xff0c; 编译后新建文件存放 访问时&#xff0c;加zl/xxx.zip

Springboot拦截器及统一异常处理

文章目录 一、Java中异常相关概念1、异常类2、异常处理方法3、注意事项4、自定义异常 二、配置全局异常处理1、统一返回体定义2、定义异常处理实现类3、全局异常处理类 三、Springboot拦截器1、定义拦截器2、注册拦截器 四、验证效果 一、Java中异常相关概念 1、异常类 Throw…

微信小程序开发系列-07组件

在开发小程序的过程中会遇到一个常见的问题&#xff0c;怎样将其他同事提供的样式或者第三方的样式载入自己的工程&#xff0c;本文基于这个问题为索引&#xff0c;探索下小程序的组件。 什么是组件 组件是视图层的基本组成单元。组件自带一些功能与微信风格一致的样式。一个…