并发编程大杀器,京东多线程编排工具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> {@Overridepublic String action(String object) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "result = " + SystemClock.now() + "---param = " + object + " from 1";}@Overridepublic String defaultValue() {return "worker1--default";}@Overridepublic void begin() {//System.out.println(Thread.currentThread().getName() + "- start --" + System.currentTimeMillis());}@Overridepublic 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
相关文章:

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

【开源项目】智慧交通~超经典开源项目实景三维数字孪生高速
数字孪生高速运营管理平台,以提升高速公路管理水平和方便出行为主要目标,充分利用云计算、AI、大数据等,实现对高速公路控制、指挥、运营的智能化。飞渡科技以实景三维数据为基础,基于大数据、高分遥感、数据分析以及数据融合等前…...

udp多播/组播那些事
多播与组播 多播(multicast)和组播(groupcast)是相同的概念,用于描述在网络中一对多的通信方式。在网络通信中,单播(unicast)是一对一的通信方式,广播(broad…...

C++ Qt开发:SqlRelationalTable关联表组件
Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍SqlRelationalTable关联表组件的常用方法及灵…...

【LeetCode】修炼之路-0001-Two Sum(两数之和)【python】【简单】
前言 计算机科学作为一门实践性极强的学科,代码能力的培养尤为重要。当前网络上有非常多优秀的前辈分享了LeetCode的最佳算法题解,这对于我们这些初学者来说提供了莫大的帮助,但对于我这种缺乏编程直觉的学习者而言,这往往难以消化吸收。(为什么别人就能想出这么优雅…...

秋招复习篇之代码规范
目录 前言 1、变量命名 2、代码空格 1)操作符左右一定有空格, 2)分隔符(, 和;)前一位没有空格,后一位保持空格,例如: 3)大括号和函数保持同一行,并有一个空格…...
Docker:登录私有仓库\退出私有仓库
一、登录仓库 docker login : 登录到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub 语法: docker login [OPTIONS] [SERVER] docker login -u 用户名 -p 密码 仓库名称 # 登入私有仓库 [rootlocalhost ~]# docker login --…...

与擎创科技共建一体化“数智”运维体系,实现数字化转型
小窗滴滴小编获取最新版公司简介 前言: 哈喽大家好,最近分享的互联网IT热讯大家都挺喜欢,小编看着数据着实开心,感谢大家支持,小编会继续给大家推送。 新岁即将启封,我们一年一期的运维干货年末大讲也要…...

开放网络+私有云=?星融元的私有云承载网络解决方案实例
在全世界范围内的云服务市场上,开放网络一直是一个备受关注的话题。相比于传统供应商的网络设备,开放网络具备软硬件解耦、云原生、可选组件丰富等优势,对云服务商和超大型企业有足够的吸引力。 SONiC作为开源的网络操作系统,使得…...

【Linux学习笔记】Linux下nginx环境搭建
1、下载nginx 安装rpm命令: rpm ivh nginx-release.rpm。(直接使用linux命令下载wget http://nginx.org/packages/rhel/6/noarch/RPMS/nginx-release-rhel-6-0.el6.ngx.noarch.rpm 2、设置nginx开机启动 chkconfig nginx on 3、开启nginx服务 方法一:service nginx…...
Python打包
将 Python 脚本打包成可执行的 .exe 文件,通常可以使用 PyInstaller 这个库来实现。PyInstaller 是一个流行的工具,它可以将 Python 程序和所有相关的依赖打包成一个独立的可执行文件,适用于 Windows、Linux 和 macOS 系统。安装 PyInstaller 首先,需要安装 PyInstaller。…...

2023启示录丨自动驾驶这一年
图片|《老人与海》插图 过去的20年,都没有2023年如此动荡。 大模型犹如一颗原子弹投入科技圈,卷起万里尘沙,传统模式瞬间被夷为平地,在耀眼的白光和巨大的轰鸣声之下,大公司、创业者、投资人甚至是每一位观…...
node实现对git仓库的管理
一、项目背景 一份代码,发布多个小程序。想让技术支持部通过脚本自己获取代码,并通过脚本自动提交到客户的小程序后台。他们自行发布。 现已注册第三方平台,开发人员通过“开发小程序”上传模板。开发人员把代码上传到模板,支持…...

『JavaScript』全面解析JavaScript中的防抖与节流技术及其应用场景
📣读完这篇文章里你能收获到 理解防抖(Debouncing)和节流(Throttling)的概念:了解这两种性能优化技术如何帮助我们更有效地处理频繁触发的事件掌握防抖与节流的实现方法:学习如何在JavaScript中…...

智能优化算法应用:基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.袋獾算法4.实验参数设定5.算法结果6.参考文献7.MA…...

Ubuntu20.04-查看GPU的使用情况及输出详解
1. 查看GPU的使用情况 1.1 nvidia-smi # 直接在终端得到显卡的使用情况 # 不会自动刷新 nvidia-smi# 重定向到文件中 nvidia-smi > nvidia_smi_output.txt# 如果输出的内容部分是以省略号表示的,可以-q nvidia-smi -q 1.2 nvidia-smi -l # 会自动刷新&#x…...
Python中的数据序列
Python中的数据序列 一、作业回顾 1、求幸运数字6 幸运数字6(只要是6的倍数):输入任意数字,如数字8,生成nums列表,元素值为1~8,从中选取幸运数字移动到新列表lucky,打印nums与lucky。 # 第一步:定义二个空列表 nums = [] lucky = [] # 第二步:提示用户输入数字 n…...

带您了解目前AI在测试领域能够解决的那些问题
AI在测试领域主要应用场景 话不多说,直接给结论: 接口测试脚本的自动生成和校验(依赖研发ai工具)测试用例的自动生成UI自动化测试脚本的自动生成和校验测试文档的自动生成快速了解初涉的业务领域 关于ai对研发和测试的整体影响…...
Jmeter学习总结(2)——时间参数化time
13位的时间戳精确都毫秒级别。 常用的时间定义格式如下: log.info("${__time(,ts)}"); log.info("${ts}"); log.info(vars.get("ts")); //136232232232log.info("${__time(yyyy-MM-dd,)}"); //当前年月日2023-12-2…...

Leetcode 746 使用最小花费爬楼梯
题意理解: 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。 一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯 目标:使用最小的花…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...

高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...