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

图片

一、简介

并发编程大杀器,京东多线程编排工具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.mfbz.cn/a/293252.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GPT/GPT4科研应用与AI绘图技术及论文高效写作(建议收藏)

详情点击链接&#xff1a;GPT/GPT4科研实践应用与AI绘图技术及论文高效写作 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Clau…

客服系统接入FastGPT

接入FastGPT 点击【应用】【外部使用】【API访问】【新建】新建一个KEY&#xff0c;同时也可以看到我们的API根地址 这个根地址和Key可以填入任何支持OpenAI接口的应用里&#xff0c;这个接口是兼容OpenAI格式。 在客服系统【知识库AI配置】里填上接口地址和接口密钥。这样我…

图像分割实战-系列教程10:U2NET显著性检测实战2

&#x1f341;&#x1f341;&#x1f341;图像分割实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 U2NET显著性检测实战1 U2NET显著性检测实战2 5、残差Unet模块 class RSU7(nn.Module):#UNet07DRES…

04.MySQL的基本操作

MySQL的基本操作 一、连接和断开MySQL服务器1、通过系统服务器启动、停止MySQL服务器2、通过命令提示符&#xff08;DOS&#xff09;启动、停止MySQL服务器2.1 启动 MySQL 服务器&#xff1a;2.2 停止 MySQL 服务器&#xff1a;2.3 登录和退出mysql 二、创建和管理数据库2.1 创…

k8s---pod基础下

k8s的pod与docker重启策略的区别 k8s的重启策略 always deployment的yaml文件只能是always&#xff0c;pod的yaml三种模式都可以。不论正常退出还是非正常退出都重启。OnFailure&#xff1a;正常退出不重启&#xff0c;非正常退出会重启Never&#xff1a;正常退出和非正常退出…

奇技淫巧:如何给项目中的RabbitMQ添加总开关

本文主要分享了如何给项目中的RabbitMQ添加总开关&#xff0c;通过简单配置开/关RabbitMQ。 一、需求背景 SpringBoot项目里使用了RabbitMQ&#xff0c;但某些场景下&#xff0c;不希望项目启动时自动检查RabbitMQ连接 例如&#xff1a; 在开发不需要RabbitMQ的功能过程中&…

Prometheus插件安装(cadvisor)

简介 当docker服务数量到一定程度&#xff0c;为了保证系统的文档&#xff0c;我们就需要对docker进行监控。一般情况下我们可以通过docker status命令来做简单的监控&#xff0c;但是无法交给prometheus采集&#xff0c;因此谷歌的cadvisor诞生了。cadvisor不仅可以轻松收集到…

【Spring进阶系列丨第六篇】Spring的Bean管理(基于注解)

文章目录 一、说明二、用于创建对象的2.1、Component注解2.1.1、定义Bean2.1.2、主配置文件配置扫描注解2.1.3、测试2.1.4、Component注解总结 2.2、Controller注解2.3、Service注解2.4、Repository注解 三、用于注入数据的3.1、Autowired注解3.1.1、定义Bean3.1.2、主配置文件…

Selenium-java元素等待三种方式

第二种方式需要写在创建driver时的代码下面 第三种则是对每个定位元素进行配置

探索Commons Exec管理外部进程

第1章&#xff1a;引言 咱们在日常的Java开发中&#xff0c;经常会遇到需要调用外部进程或命令的场景。比如说&#xff0c;可能需要在Java程序中启动一个外部的脚本&#xff0c;或者执行一个系统命令。Java虽然提供了Runtime和ProcessBuilder类来处理这类需求&#xff0c;但说…

Docker Linux快速安装及Nginx部署

前言 最近正在部署一套新的Linux服务器环境&#xff0c;基于Docker来部署所有的应用&#xff0c;顺便整理了一套经过验证的操作手册&#xff0c;以便大家遇到类似需求时&#xff0c;可以直接拿来用。 本文会涉及以下知识点&#xff1a;Docker的Linux安装和卸载、Docker用户组…

【网络安全】Nessus部署自动更新和端口权限开放

文章目录 Nessus 自动更新配置Nessus服务端口开放Nessus profession 版本需要开放端口Sensor ProxyTenable Security Center (TSC)Tenable OT Security (TOT)Tenable OT Security Enterprise Manager (IEM)Tenable OT Security Industrial Core Platform (ICP)Tenable OT Secur…

基于卷积神经网络的回归分析

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 卷积神经网络的回归分析 完整代码:卷积神经网络的回归分析(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/…

基于深度学习大模型实现离线翻译模型私有化部署使用,通过docker打包开源翻译模型,可到内网或者无网络环境下运行使用,可以使用一千多个翻译模型语言模型进行翻译

基于深度学习大模型实现离线翻译模型私有化部署使用,通过docker打包开源翻译模型,可到内网或者无网络环境下运行使用,可以使用一千多个翻译模型语言模型进行翻译,想要什么语种直接进行指定和修改就行。 环境要求,电脑内存低于8G建议不要尝试了,有无GPU都可以运行,但是有…

秋招复习之栈与队列

前言 1 栈 「栈 stack」是一种遵循先入后出逻辑的线性数据结构。 我们可以将栈类比为桌面上的一摞盘子&#xff0c;如果想取出底部的盘子&#xff0c;则需要先将上面的盘子依次移走。我们将盘子替换为各种类型的元素&#xff08;如整数、字符、对象等&#xff09;&#xff0c…

释放创造力:可视化页面渲染引擎在低代码开发平台的应用

本文由葡萄城技术团队发布。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 什么是页面渲染引擎? 页面渲染引擎是低代码开发平台的核心组件之一&#xff0c;它负责将开发者设计的页面布局和用户…

Docker 存储卷管理

一、存储卷简介 存储卷是一种方便、灵活、高效的Docker容器内数据存储方式。存储卷可以在容器内的不同进程间共享数据&#xff0c;并且可以在容器之间共享和重用。 二、存储卷的优点 可以在容器之间共享和重用&#xff0c;避免了在不同容器之间复制数据的繁琐。对数据卷的修…

Flume基础知识(七):Flume 事务与 Flume Agent 内部原理

1. Flume 事务详解 2. Flume Agent 内部原理 重要组件&#xff1a; 1&#xff09;ChannelSelector ChannelSelector 的作用就是选出 Event 将要被发往哪个 Channel。其共有两种类型&#xff0c; 分别是 Replicating&#xff08;复制&#xff09;和 Multiplexing&#xff08;多…

linux 的直接direct io

目录 什么是 Direct IO java 支持 使用场景 数据库 反思 在之前的文章零拷贝基础上&#xff0c;有一个针对那些不需要在操作系统的 page cache 里保存的情况&#xff0c;即绕过 page cache&#xff0c;对于 linux 提供了 direct io 的功能。 https://blog.csdn.net/zlpzl…

芯课堂 | LVG免费开源GUI图形库

概述 本文介绍目前LVGL的应用小知识&#xff0c;希望对采用MCU设计UI界面的用户有所启发&#xff0c;开发出界面更友好的消费品或者工业产品&#xff0c;造福大众。 01.LVGL系统架构 LVGL系统框架 应用程序创建GUI并处理特定任务的应用程序。 LVGL本身是一个图形库。我们的…