网易首页 > 网易号 > 正文 申请入驻

基于DAG的任务编排框架/平台

0
分享至

最近在做的工作比较需要一个支持任务编排工作流的框架或者平台,这里记录下实现上的一些思路。


任务编排工作流

任务编排是什么意思呢,顾名思义就是可以把"任务"这个原子单位按照自己的方式进行编排,任务之间可能互相依赖。复杂一点的编排之后就能形成一个 workflow 工作流了。我们希望这个工作流按照我们编排的方式去执行每个原子 task 任务。如下图所示,我们希望先并发运行 Task A 和 Task C,Task A 执行完后串行运行 Task B,再并发等待 Task B 和 C 都结束后运行 Task D,这样就完成了一个典型的任务编排工作流。

DAG 有向无环图

首先我们了解图这个数据结构,每个元素称为顶点 vertex,顶点之间的连线称为边 edge。像我们画的这种带箭头关系的称为有向图,箭头关系之间能形成一个环的称为有环图,反之称为无环图。显然运用在我们任务编排工作流上,最合适的是 DAG 有向无环图。

我们在代码里怎么存储图呢,有两种数据结构:邻接矩阵和邻接表。

下图表示一个有向图的邻接矩阵,例如 x->y 的边,只需将 Array[x][y]标识为 1 即可。

此外我们也可以使用邻接表来存储,这种存储方式较好地弥补了邻接矩阵浪费空间的缺点,但相对来说邻接矩阵能更快地判断连通性。

一般在代码实现上,我们会选择邻接矩阵,这样我们在判断两点之间是否有边更方便点。

一个任务编排框架

了解了 DAG 的基本知识后我们可以来简单实现一下。

了解JUC包的可能快速想到CompletableFuture,这个类对于多个并发线程有复杂关系耦合的场景是很适用的,如果是一次性任务,那么使用CompletableFuture完全没有问题。但是作为框架或者平台来说,我们还需要考虑存储节点状态、重试执行等逻辑,对于这些CompletableFuture是不能满足的。

我们需要更完整地考虑与设计这个框架。首先是存储结构,我们的 Dag 表示一整个图,Node 表示各个顶点,每个顶点有其 parents 和 children:

//Dagpublic final class DefaultDag implements Dag { private Map> nodes = new HashMap>();
}//Nodepublic final class Node { /**
* incoming dependencies for this node
private Set> parents = new LinkedHashSet>(); /**
* outgoing dependencies for this node
private Set> children = new LinkedHashSet>();

画两个顶点,以及为这两个顶点连边操作如下:

public void addDependency(final T evalFirstNode, final T evalLaterNode) {
Node firstNode = createNode(evalFirstNode);
Node afterNode = createNode(evalLaterNode);

addEdges(firstNode, afterNode);
}
private Node createNode(final T value) {
Node node = new Node(value); return node;
}private void addEdges(final Node firstNode, final Node afterNode) { if (!firstNode.equals(afterNode)) {
firstNode.getChildren().add(afterNode);
afterNode.getParents().add(firstNode);
}
}

到现在我们其实已经把基础数据结构写好了,但我们作为一个任务编排框架最终是需要线程去执行的,我们把它和线程池一起给包装一下。

//任务编排线程池public class DefaultDexecutor { //执行线程,和2种重试线程
private final ExecutorService executionEngine; private final ExecutorService immediatelyRetryExecutor; private final ScheduledExecutorService scheduledRetryExecutor; //执行状态
private final ExecutorState state;
}//执行状态public class DefaultExecutorState { //底层图数据结构
private final Dag graph; //已完成
private final Collection> processedNodes; //未完成
private final Collection> unProcessedNodes; //错误task
private final Collection> erroredTasks; //执行结果
private final Collection> executionResults;

可以看到我们的线程包括执行线程池,2 种重试线程池。我们使用 ExecutorState 来保存一些整个任务工作流执行过程中的一些状态记录,包括已完成和未完成的 task,每个 task 执行的结果等。同时它也依赖我们底层的图数据结构 DAG。

接下来我们要做的事其实很简单,就是 BFS 这整个 DAG 数据结构,然后提交到线程池中去执行就可以了,过程中注意一些节点状态的保持,结果的保存即可。

还是以上图为例,值得说的一点是在 Task D 这个点需要有一个并发等待的操作,即 Task D 需要依赖 Task B 和 Task C 执行结束后再往下执行。这里有很多办法,我选择了共享变量的方式来完成并发等待。遍历工作流中被递归的方法的伪代码如下:

private void doProcessNodes(final Set> nodes) { for (Node node : nodes) { //共享变量 并发等待
if (!processedNodes.contains(node) && processedNodes.containsAll(node.getParents())) {
Task task = newTask(node); this.executionEngine.submit(task);
ExecutionResult executionResult = this.executionEngine.processResult(); if (executionResult.isSuccess()) {
state.markProcessingDone(processedNode);
} //继续执行孩子节点
doExecute(processedNode.getChildren());

这样我们基本完成了这个任务编排框架的工作,现在我们可以如下来进行示例图中的任务编排以及执行:

DefaultExecutor executor = newTaskExecutor();
executor.addDependency("A", "B");
executor.addDependency("B", "D");
executor.addDependency("C", "D");
executor.execute(); 任务编排化平台

好了现在我们已经有一款任务编排框架了,但很多时候我们想要可视化、平台化,让使用者更加无脑。

框架与平台最大的区别在哪里?是可拖拽的可视化输入么?我觉得这个的复杂度更多在前端。而对于后端平台来讲,与框架最大的区别是数据的持久化。

对于 DAG 的节点来说,我们需要将每个节点 Task 的信息给持久化到关系数据库中,包括 Task 的状态、输出结果等。而对于 DAG 的边来说,我们也得用数据库来存储各 Task 之间的方向关系。此外,在遍历执行 DAG 的整个过程中的中间状态数据,我们也得搬运到数据库中。

首先我们可以设计一个 workflow 表,来表示一个工作流。接着我们设计一个 task 表,来表示一个执行单元。task 表主要字段如下,这里主要是 task_parents 的设计,它是一个 string,存储 parents 的 taskId,由多个分隔符分隔。

task_id
workflow_id
task_name
task_status
result
task_parents

依赖是上图这个例子,对比框架来说,我们首先得将其存储到数据库中去,最终可能得到如下数据:task_id workflow_id task_name task_status result task_parents
1 1 A 0 -1
2 1 B 0 1
3 1 C 0 -1
4 1 D 0 2,3

可以看到,这样也能很好地存储 DAG 数据,和框架中代码的输入方式差别并不是很大。

接下来我们要做的是遍历执行整个 workflow,这边和框架的差别也不大。首先我们可以利用select * from task where workflow_id = 1 and task_parents = -1来获取初始化节点 Task A 和 Task C,将其提交到我们的线程池中。

接着对应框架代码中的doExecute(processedNode.getChildren());,我们使用select * from task where task_parents like %3%,就可以得到 Task C 的孩子节点 Task D,这里使用了模糊查询是因为我们的 task_parents 可能是由多个父亲的 taskId 与分隔号组合而成的字符串。查询到孩子节点后,继续提交到线程池即可。

别忘了我们在 Task D 这边还有一个并发等待的操作,对应框架代码中的if (!processedNodes.contains(node) && processedNodes.containsAll(node.getParents()))。这边我们只要判断select count(1) from task where task_id in (2,3) and status != 1的个数为 0 即可,即保证 parents task 全部成功。

另外值得注意的是 task 的重试。在框架中,失败 task 的重试可以是立即使用当前线程重试或者放到一个定时线程池中去重试。而在平台上,我们的重试基本上来自于用户在界面上的点击,即主线程。

至此,我们已经将任务编排框架的功能基本平台化了。作为一个任务编排平台,可拖拽编排的可视化输入、整个工作流状态的可视化展示、任务的可人工重试都是其优点。

来源https://fredal.xin/task-scheduling-based-on-dag

更多推荐

消息系统架构设计演进

优化架构设计的 10 个微服务最佳实践

架构和架构师的不同类型

高可用Redis服务架构分析与搭建

9 张图带你深入理解 Docker 架构!

免责声明:

本公众号部分分享的资料来自网络收集和整理,所有文字和图片版权归属于原作者所有,且仅代表作者个人观点,与本公众号无关,文章仅供读者学习交流使用,并请自行核实相关内容,如文章内容涉及侵权,请联系后台管理员删除。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
大家目前都穷到什么境界了?评论区一个比一个惨啊

大家目前都穷到什么境界了?评论区一个比一个惨啊

阿康四岁啦
2024-06-15 17:37:07
金灿荣:两国关系跌进谷底!中印矛盾已久,印度野心不容小觑

金灿荣:两国关系跌进谷底!中印矛盾已久,印度野心不容小觑

前沿讲座课堂
2023-04-28 16:04:32
新能源车渗透率都过50%了,为什么没见过专修新能源车的修理厂?

新能源车渗透率都过50%了,为什么没见过专修新能源车的修理厂?

音乐时光的娱乐
2024-06-18 01:27:18
姑娘大意了,镜片里的反射早被大家看得一清二楚,不觉得尴尬吗?

姑娘大意了,镜片里的反射早被大家看得一清二楚,不觉得尴尬吗?

水泥土的搞笑
2024-06-18 07:05:08
津门虎刚完败上港,于根伟就敲定重量级外援加盟,曾是巴萨主力

津门虎刚完败上港,于根伟就敲定重量级外援加盟,曾是巴萨主力

评球论事
2024-06-17 18:35:23
太意外了,仁爱礁坐滩军舰之所以没有快速解体,居然是它在搞鬼!

太意外了,仁爱礁坐滩军舰之所以没有快速解体,居然是它在搞鬼!

老妖的针见
2024-06-14 14:20:02
带着电影一起飞,“文旅商体展”超级大融合

带着电影一起飞,“文旅商体展”超级大融合

新民周刊
2024-06-16 09:08:01
国足痛失天才球员,国足名宿之子选择为日本效力,成日本豪门核心

国足痛失天才球员,国足名宿之子选择为日本效力,成日本豪门核心

小豆豆赛事
2024-04-24 07:30:46
将近40岁满脸褶,却尬演18岁少女,是谁给了她“强行装嫩”的勇气

将近40岁满脸褶,却尬演18岁少女,是谁给了她“强行装嫩”的勇气

娱乐圈十三太保
2024-05-28 13:56:53
中方对澳免签后,澳总理当面做重要承诺,并为中国人入境提供便利

中方对澳免签后,澳总理当面做重要承诺,并为中国人入境提供便利

王墨观察
2024-06-17 17:27:00
以色列已陷入车轮战

以色列已陷入车轮战

文雅笔墨
2024-06-17 06:36:07
女排头牌太暖心 朱婷赛后脱掉外套递给新星 后者受宠若惊

女排头牌太暖心 朱婷赛后脱掉外套递给新星 后者受宠若惊

厝边人侃体育
2024-06-17 20:54:28
伊万专访(下):更需要热爱中国的归化球员;目标是进军世界杯

伊万专访(下):更需要热爱中国的归化球员;目标是进军世界杯

懂球帝
2024-06-17 11:09:14
最让你感动的胜利是?穆里尼奥:不是夺欧冠,而是率罗马夺欧会杯

最让你感动的胜利是?穆里尼奥:不是夺欧冠,而是率罗马夺欧会杯

直播吧
2024-06-17 18:38:02
蔡英文在上海的老照片:42岁看起来像22岁,漂亮温柔,气质独特

蔡英文在上海的老照片:42岁看起来像22岁,漂亮温柔,气质独特

黄河新流域
2024-05-24 21:10:02
镜报:哈兰德在西班牙度假时遇防爆警察突击检查,被要求掏身份证

镜报:哈兰德在西班牙度假时遇防爆警察突击检查,被要求掏身份证

直播吧
2024-06-17 19:09:11
开始反击!中国宣布对欧美国家断供盾构机,这次轮到他们被卡脖子

开始反击!中国宣布对欧美国家断供盾构机,这次轮到他们被卡脖子

米果说识
2024-06-17 16:47:05
汽车流通协会会长:全世界汽车价格都向上走,赚的盆满钵满!只有中国价格战在向下走,很奇怪

汽车流通协会会长:全世界汽车价格都向上走,赚的盆满钵满!只有中国价格战在向下走,很奇怪

和讯网
2024-06-17 11:04:39
江苏男子罗布泊探险后精神失常,临终坦白真相:回来的不是人

江苏男子罗布泊探险后精神失常,临终坦白真相:回来的不是人

青丝人生
2024-06-11 18:49:55
图纸也打马赛克,日本公司也不行了,格局碎了一地

图纸也打马赛克,日本公司也不行了,格局碎了一地

CAD画家
2024-06-18 07:00:24
2024-06-18 08:58:44
IT架构师联盟
IT架构师联盟
IT架构实战分享
688文章数 7654关注度
往期回顾 全部

科技要闻

低价“6·18”没有狂欢

头条要闻

外媒炒作:中国首次将核弹头置于高度战备状态

头条要闻

外媒炒作:中国首次将核弹头置于高度战备状态

体育要闻

24年后,他们终于又在欧洲杯赢球了

娱乐要闻

上影节红毯:倪妮好松弛,娜扎吸睛

财经要闻

广汽也想“掀桌子”了?

汽车要闻

传奇新篇章 全新一代大众迈腾来了

态度原创

艺术
教育
本地
公开课
军事航空

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

教育要闻

难倒全班同学的一道小学数学思维训练题,符号的运算规矩

本地新闻

能动司法尽“执”履责 ——“交叉执行”高效能

公开课

近视只是视力差?小心并发症

军事要闻

"局部战术暂停"后 以军袭击加沙地带多地

无障碍浏览 进入关怀版