01 引言
本文为
Apache DolphinScheduler
官网的阅读笔记,原文地址:https://dolphinscheduler.apache.org/
在前面的博文《Apache DolphinScheduler 入门(一篇就够了)》大致讲了DolphinScheduler
的一些概念,有了一个入门的概念。本文主要讲的是DolphinScheduler
的系统架构设计。
本文开始讲解前,需要了解一些基础的概念:
- DAG: 全称
Directed Acyclic Graph
,简称DAG
。工作流中的Task
任务以有向无环图的形式组装起来,从入度为零的节点进行拓扑遍历,直到无后继节点为止。举例如下图: - 流程定义:通过拖拽任务节点并建立任务节点的关联所形成的可视化
DAG
; - 流程实例:流程实例是流程定义的实例化,可以通过手动启动或定时调度生成,流程定义每运行一次,产生一个流程实例;
- 任务实例:任务实例是流程定义中任务节点的实例化,标识着具体的任务执行状态;
- 任务类型: 目前支持有
SHELL
、SQL
、SUB_PROCESS
(子流程)、PROCEDURE
、MR
、SPARK
、PYTHON
、DEPENDENT
(依赖),同时计划支持动态插件扩展,注意:其中子SUB_PROCESS
也是一个单独的流程定义,是可以单独启动执行的; - 调度方式: 系统支持基于
cron
表达式的定时调度和手动调度。命令类型支持:启动工作流、从当前节点开始执行、恢复被容错的工作流、恢复暂停流程、从失败节点开始执行、补数、定时、重跑、暂停、停止、恢复等待线程。其中 恢复被容错的工作流 和 恢复等待线程 两种命令类型是由调度内部控制使用,外部无法调用; - 定时调度:系统采用
quartz
分布式调度器,并同时支持cron
表达式可视化的生成; - 依赖:系统不单单支持
DAG
简单的前驱和后继节点之间的依赖,同时还提供任务依赖节点,支持流程间的自定义任务依赖; - 优先级 :支持流程实例和任务实例的优先级,如果流程实例和任务实例的优先级不设置,则默认是先进先出;
- 邮件告警:支持
SQL
任务 查询结果邮件发送,流程实例运行结果邮件告警及容错告警通知; - 失败策略:对于并行运行的任务,如果有任务失败,提供两种失败策略处理方式,继续是指不管并行运行任务的状态,直到流程失败结束。结束是指一旦发现失败任务,则同时
Kill
掉正在运行的并行任务,流程失败结束; - 补数:补历史数据,支持区间并行和串行两种补数方式;
02 系统架构
2.1 MasterServer
MasterServer
采用分布式无中心设计理念,MasterServer
主要负责 DAG
任务切分、任务提交监控,并同时监听其它MasterServer
和WorkerServer
的健康状态。
MasterServer
服务启动时向Zookeeper
注册临时节点,通过监听Zookeeper
临时节点变化来进行容错处理。
MasterServer服务内主要包含:
- Distributed Quartz分布式调度组件:主要负责定时任务的启停操作,当quartz调起任务后,Master内部会有线程池具体负责处理任务的后续操作;
- MasterSchedulerThread:是一个扫描线程定时扫描数据库中的 command 表,根据不同的命令类型进行不同的业务操作;
- MasterExecThread:主要是负责DAG任务切分、任务提交监控、各种不同命令类型的逻辑处理;
- MasterTaskExecThread:主要负责任务的持久化;
2.2 WorkerServer
WorkerServer
也采用分布式无中心设计理念,WorkerServer主要负责任务的执行和提供日志服务。WorkerServer服务启动时向Zookeeper注册临时节点,并维持心跳。
该服务包含:
- FetchTaskThread:主要负责不断从
Task Queue
中领取任务,并根据不同任务类型调用TaskScheduleThread
对应执行器; - LoggerServer:是一个
RPC
服务,提供日志分片查看、刷新和下载等功能。
2.3 ZooKeeper
ZooKeeper
服务,系统中的MasterServer
和WorkerServer
节点都通过ZooKeeper
来进行集群管理和容错。另外系统还基于ZooKeeper进行事件监听和分布式锁。 我们也曾经基于Redis实现过队列,不过我们希望EasyScheduler依赖到的组件尽量地少,所以最后还是去掉了Redis实现。
2.4 Task Queue
提供任务队列的操作,目前队列也是基于Zookeeper来实现。由于队列中存的信息较少,不必担心队列里数据过多的情况,实际上压测过百万级数据存队列,对系统稳定性和性能没影响。
2.5 Alert
提供告警相关接口,接口主要包括告警两种类型的告警数据的存储、查询和通知功能。其中通知功能又有邮件通知和 SNMP(暂未实现) 两种。
2.6 API
API接口层,主要负责处理前端UI层的请求。该服务统一提供RESTful api向外部提供请求服务。 接口包括工作流的创建、定义、查询、修改、发布、下线、手工启动、停止、暂停、恢复、从该节点开始执行等等。
2.7 UI
系统的前端页面,提供系统的各种可视化操作界面。
04 架构思想
4.1 中心化思想
中心化的设计理念比较简单,分布式集群中的节点按照角色分工,大体上分为两种角色:
- Master的角色主要负责任务分发并监督Slave的健康状态,可以动态的将任务均衡到Slave上,以致Slave节点不至于“忙死”或”闲死”的状态。
- Worker的角色主要负责任务的执行工作并维护和Master的心跳,以便Master可以分配任务给Slave。
中心化思想设计存在的问题:
- 一旦
Master
出现了问题,则群龙无首,整个集群就会崩溃。为了解决这个问题,大多数Master/Slave
架构模式都采用了主备Master
的设计方案,可以是热备或者冷备,也可以是自动切换或手动切换,而且越来越多的新系统都开始具备自动选举切换Master
的能力,以提升系统的可用性。 - 另外一个问题是如果
Scheduler
在Master
上,虽然可以支持一个DAG
中不同的任务运行在不同的机器上,但是会产生Master
的过负载。如果Scheduler
在Slave
上,则一个DAG
中所有的任务都只能在某一台机器上进行作业提交,则并行任务比较多的时候,Slave
的压力可能会比较大。
4.2 去中心化思想
在去中心化设计里,通常没有Master/Slave的概念,所有的角色都是一样的,地位是平等的,全球互联网就是一个典型的去中心化的分布式系统,联网的任意节点设备down机,都只会影响很小范围的功能。
去中心化设计的核心设计在于整个分布式系统中不存在一个区别于其他节点的”管理者”,因此不存在单点故障问题。但由于不存在” 管理者”节点所以每个节点都需要跟其他节点通信才得到必须要的机器信息,而分布式系统通信的不可靠行,则大大增加了上述功能的实现难度。
实际上,真正去中心化的分布式系统并不多见。反而动态中心化分布式系统正在不断涌出。在这种架构下,集群中的管理者是被动态选择出来的,而不是预置的,并且集群在发生故障的时候,集群的节点会自发的举行"会议"来选举新的"管理者"去主持工作。最典型的案例就是ZooKeeper及Go语言实现的Etcd。
EasyScheduler的去中心化是Master/Worker注册到Zookeeper中,实现Master集群和Worker集群无中心,并使用Zookeeper分布式锁来选举其中的一台Master或Worker为“管理者”来执行任务。
4.3 分布式锁实践
EasyScheduler
使用ZooKeeper
分布式锁来实现同一时刻只有一台Master
执行Scheduler
,或者只有一台Worker
执行任务的提交。
获取分布式锁的核心流程算法如下:
EasyScheduler中Scheduler线程分布式锁实现流程图: