Dolphinscheduler Master 核心执行流程源码分析

Dolphinscheduler Master 节点的主要工作是掌控整个 DAG 工作流的生命周期,决定任务的执行顺序,分发任务至合适的 worker 节点,及时更新任务的状态信息。(文章源码基于dolphinscheduler 3.1.8)

  1. 用户手动触发任务或任务定时调起时会根据任务相关信息,创建指令插入数据库。该指令中包含任务发起所需要的关键信息,如任务定义 id,执行参数,指令类型等。
commandMapper.insert(command);
  1. MasterSchedulerBootstrap 是 Master 的一个调度线程,主要工作是从数据库中查询待执行的指令,根据指令信息封装任务执行线程实例,添加到队列中等待执行。该线程在 Master 服务启动时跟随启动。
// 调度线程启动
this.masterSchedulerBootstrap.start();
// 查询待执行的指令
List<Command> commands = findCommands();

// 指令会转换为相应的工作流实例,同时创建任务执行线程实例,缓存到processInstanceExecCacheManager,接着新建一个工作流事件添加到队列WorkflowEventQueue等待出列
WorkflowExecuteRunnable workflowRunnable = new WorkflowExecuteRunnable(processInstance,
        processService,
        processInstanceDao,
        nettyExecutorManager,
        processAlertManager,
        masterConfig,
        stateWheelExecuteThread,
        curingGlobalParamsService);
processInstanceExecCacheManager.cache(processInstance.getId(), workflowRunnable);
workflowEventQueue.addEvent(new WorkflowEvent(WorkflowEventType.START_WORKFLOW, processInstance.getId()));

// 该调度线程启动时,同时会启动WorkflowEventLooper
workflowEventLooper.start();
  1. WorkflowEventLooper 是一个轮询线程,负责从 WorkflowEventQueue 队列中获取事件去执行。
// 获取一个事件
workflowEvent = workflowEventQueue.poolEvent();
// 根据事件类型,获取对应的事件处理器处理事件。例:工作流事件类型:START_WORKFLOW, 对应的处理器WorkflowStartEventHandler
WorkflowEventHandler workflowEventHandler =workflowEventHandlerMap.get(workflowEvent.getWorkflowEventType());
workflowEventHandler.handleWorkflowEvent(workflowEvent);

其中 workflowEventHandlerMap 在程序启动时初始化。


public interface WorkflowEventHandler {
    void handleWorkflowEvent(WorkflowEvent workflowEvent) throws WorkflowEventHandleError, WorkflowEventHandleException;
    WorkflowEventType getHandleWorkflowEventType();
}

@Component
public class WorkflowStartEventHandler implements WorkflowEventHandler {
}

@Component
public class WorkflowEventLooper {
    @Autowired
    private List<WorkflowEventHandler> workflowEventHandlerList;

    @PostConstruct
    public void init() {
        workflowEventHandlerList.forEach(
                workflowEventHandler -> workflowEventHandlerMap.put(workflowEventHandler.getHandleWorkflowEventType(),
                        workflowEventHandler));
    }
}
  1. WorkflowStartEventHandler 处理器会从缓存 processInstanceExecCacheManager 中拿到对应的任务执行线程实例 workflowExecuteRunnable,然后提交到异步线程池执行。
CompletableFuture.supplyAsync(workflowExecuteRunnable::call, workflowExecuteThreadPool).thenAccept();
  1. 接下来就是最核心的线程类 WorkflowExecuteRunnable。 主要用来解析 dag,负责 dag 的整个调度流程,包括任务的初始化、提交、分发、执行控制,任务各类事件的处理等。
// 解析dag
buildFlowDag();
// 初始化任务执行队列
initTaskQueue();
// 提交任务执行
submitTaskExec(TaskInstance taskInstance)

submitTaskExec 会根据 taskInstance 的类型去选择不同的任务处理器,不同的任务处理器针对不同的 action 类型有不同的实现方式。例如 shell、python 这类通用任务会选择默认任务处理器 CommonTaskProcessor,这类任务提交后 Master 无需其他操作,直接派发到 worker 执行,而 dependent 任务选择 DependentTaskProcessor,这类任务仅需要 Master 判断依赖的任务是否执行成功,而无需派发到 worker 节点执行。

// 获取任务处理器
ITaskProcessor taskProcessor = TaskProcessorFactory.getTaskProcessor(taskInstance.getTaskType());
// 依次执行对应的操作:提交、派发、执行等
taskProcessor.action(TaskAction.SUBMIT);
taskProcessor.action(TaskAction.DISPATCH);
taskProcessor.action(TaskAction.RUN);

任务处理器插件 TaskProcessor 实现方式。


// 任务处理器接口,定义action方法,处理对任务的不同操作
public interface ITaskProcessor extends PrioritySPI {
    boolean action(TaskAction taskAction);
}

// 基础任务处理器,根据不同的action类型执行不同的动作方法,不同的任务处理器针对不同的动作有各自的实现逻辑
public abstract class BaseTaskProcessor implements ITaskProcessor {
    @Override
    public boolean action(TaskAction taskAction) {
        switch (taskAction) {
                case SUBMIT:
                    result = submit();
                    break;
                case RUN:
                    result = run();
                    break;
                case DISPATCH:
                    result = dispatch();
                    break;
                default:
                    logger.error("unknown task action: {}", taskAction);
            }
    }
    // 定义了公共的抽象行为,子类需要按照各自的逻辑去实现
    protected abstract boolean submitTask();
    protected abstract boolean runTask();
    protected abstract boolean dispatchTask();
}

// 具体的任务处理器子类
// 通用的任务处理器
@AutoService(ITaskProcessor.class)
public class CommonTaskProcessor extends BaseTaskProcessor {
    @Override
    protected boolean submitTask() {
        // 核心逻辑只需要创建任务实例入库
        TaskInstance task = submitTaskInstanceToDB(taskInstance, processInstance);
    }
    @Override
    public boolean dispatchTask() {
        // 初始化队列,该队列是一个带有优先级的阻塞队列,由PriorityBlockingQueue实现,确保并发安全,并可以自定义任务的优先级
        this.taskUpdateQueue = SpringApplicationContext.getBean(TaskPriorityQueueImpl.class);
        // 创建带优先级的任务
        TaskPriority taskPriority = new TaskPriority()
        // 任务push至优先级阻塞队列中
        taskUpdateQueue.put(taskPriority);
    }
    @Override
    public boolean runTask() {
        // 具体的任务分发至worker节点执行,这里无需其他逻辑
        return true;
    }
}
// Dependent任务处理器
@AutoService(ITaskProcessor.class)
public class DependentTaskProcessor extends BaseTaskProcessor {
 @Override
    public boolean submitTask() {
        TaskInstance task = submitTaskInstanceToDB(taskInstance, processInstance);
        // 除了创建任务实例插入数据库外,还需要初始化dependetn任务的参数
        initDependParameters();
    }

    @Override
    public boolean runTask() {
        // 具体的执行逻辑,需要判断所有的任务依赖是否都执行成功
        if (!allDependentItemFinished) {
            allDependentItemFinished = allDependentTaskFinish();
        }
        if (allDependentItemFinished) {
            getTaskDependResult();
            endTask();
        }
        return true;
    }
    @Override
    protected boolean dispatchTask() {
        // 无需下发到worker节点去执行任务
        return true;
    }
}


这里仅列举了典型的两种任务处理器,除此之外,还有 SwitchTaskProcessor, ConditionTaskProcessor 等。

  1. 需要 worker 节点执行的任务,会派发到 TaskPriorityQueue 类型的队列中,TaskPriorityQueueConsumer 是一个消费线程,他会从队列中取出任务,根据一定的负载均衡策略分发到指定的 worker 节点执行。
public class TaskPriorityQueueConsumer extends BaseDaemonThread {
    // 存储任务实例的队列
    @Autowired
    private TaskPriorityQueue<TaskPriority> taskPriorityQueue;

    @Override
    public void run() {
        // 取出任务
        TaskPriority taskPriority = taskPriorityQueue.poll(Constants.SLEEP_TIME_MILLIS, TimeUnit.MILLISECONDS);
        // 根据负载均衡策略选择具体执行任务的worker节点
        Host host = hostManager.select(context);
        // 利用rpc服务发送任务至worker
        nettyRemotingClient.send(host, command);
    }
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于DolphinScheduler源码分析,我可以给你一些大致的指导。DolphinScheduler是一款开源的分布式任务调度系统,主要用于大数据领域的任务调度和数据处理。它采用了分布式架构,支持高可用性和高可扩展性。 首先,你可以从DolphinScheduler的GitHub仓库中获取源代码:https://github.com/apache/incubator-dolphinscheduler源码中,你可以先从入口开始分析,主要涉及到的类是`org.apache.dolphinscheduler.server.master.MasterServerApplication`和`org.apache.dolphinscheduler.server.worker.WorkerServerApplication`,它们分别是Master节点和Worker节点的入口。 从Master节点入口开始,你可以深入了解Master节点的整体架构和实现。一些关键的类包括`org.apache.dolphinscheduler.server.master.MasterSchedulerService`、`org.apache.dolphinscheduler.server.master.runner.MasterSchedulerThread`和`org.apache.dolphinscheduler.server.master.cache.ScheduleService`等。 在Worker节点方面,你可以关注`org.apache.dolphinscheduler.server.worker.runner.WorkerManager`、`org.apache.dolphinscheduler.server.worker.runner.WorkerExecProcessor`和`org.apache.dolphinscheduler.server.worker.cache.LocalTaskCacheManager`等类,它们负责Worker节点的任务执行和管理。 此外,还有一些其他重要的模块需要进行源码分析,比如任务调度算法、任务执行环境配置、任务依赖处理、日志管理等。 在进行源码分析时,你可以结合官方提供的文档和注释,以及各个类和方法的调用关系来理清思路。可以通过IDE的调试功能和日志输出来帮助你更好地理解源码执行流程和逻辑。 总体来说,DolphinScheduler源码分析需要一定的时间和精力,建议你先对整体架构和关键模块有一个整体的了解,然后再深入到具体的实现细节。希望这些信息对你有所帮助,祝你顺利进行源码分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值