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

Dolphinscheduler worker 节点的主要工作就是任务执行。worker 节点注册了各类任务执行插件,根据 master 节点派发任务的具体类型选择具体的插件执行。(文章源码基于 Dolphinscheduler3.1.8)

  1. TaskDispatchProcessor 用来处理 master 派发的具体任务。
// 创建任务执行线程实例
WorkerDelayTaskExecuteRunnable workerTaskExecuteRunnable = WorkerTaskExecuteRunnableFactoryBuilder
                    .createWorkerDelayTaskExecuteRunnableFactory(
                            taskExecutionContext,
                            workerConfig,
                            workflowMasterAddress,
                            workerMessageSender,
                            alertClientService,
                            taskPluginManager,
                            storageOperate)
                    .createWorkerTaskExecuteRunnable();
// 提交任务到任务管理器
workerManager.offer(workerTaskExecuteRunnable);
  1. WorkerManagerThread 是一个任务管理线程,其实就是持有任务线程实例队列 waitSubmitQueue 和任务执行器 workerExecService,从前者取出任务提交到后者去执行。
// 该线程会跟随worker节点创建时启动
this.workerManagerThread.start();

@Component
public class WorkerManagerThread implements Runnable {
    // 延迟任务队列
    private final DelayQueue<WorkerDelayTaskExecuteRunnable> waitSubmitQueue;
    // 任务执行器
    private final WorkerExecService workerExecService;
     @Override
    public void run() {
        // 取出一个任务
        waitSubmitQueue.take();
        // 提交到任务执行器执行
        workerExecService.submit(workerDelayTaskExecuteRunnable);
    }
}
  1. WorkerTaskExecuteRunnable 是实际的任务执行线程。实现一些公用的逻辑,主要任务逻辑会在具体的任务插件中执行。
// beforeExecute方法:根据任务类型获取具体的任务工厂创建任务,并执行任务的初始化方法
TaskChannel taskChannel = taskPluginManager.getTaskChannelMap().get(taskExecutionContext.getTaskType());
task = taskChannel.createTask(taskExecutionContext);
task.init();

// executeTask方法:执行任务的handle方法,即具体任务的执行逻辑
task.handle(taskCallBack);

// afterExecute方法:处理任务执行的结果
sendTaskResult();

WorkerManagerThread 在这里是个抽象类,实际上推送到队列的是他的子类 WorkerDelayTaskExecuteRunnable 类型的实例,该类实现了 Delayed 接口,实现了 getDelayed 和 compareTo 方法,且这里的队列是一个 DelayedQueue,从而使派发的 worker 节点的任务可以延迟执行。

public abstract class WorkerDelayTaskExecuteRunnable extends WorkerTaskExecuteRunnable implements Delayed {
    // 获取剩余延迟时间
    @Override
    public long getDelay(TimeUnit unit) {
        TaskExecutionContext taskExecutionContext = getTaskExecutionContext();
        return unit.convert(
                DateUtils.getRemainTime(
                        taskExecutionContext.getFirstSubmitTime(), taskExecutionContext.getDelayTime() * 60L), TimeUnit.SECONDS);
    }
    // 选出剩余延迟时间小的任务先执行
    @Override
    public int compareTo(Delayed o) {
        if (o == null) {
            return 1;
        }
        return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
    }
}

  1. TaskPluginManager 任务管理器插件,负责任务插件的管理,任务的创建等。具体的各类任务插件通过 Java SPI 的方式实现,会在应用启动的时候初始化到任务插件管理器中。具体的实现方式如下:
// 各SPI插件的“身份证”,记录插件的名称和优先级
public class SPIIdentify {
    // 默认优先级
    private static final int DEFAULT_PRIORITY = 0;
    // 插件名称
    private String name;
    // 优先级
    private int priority = DEFAULT_PRIORITY;
}

// SPI插件优先级接口,实现该接口的插件可以按照优先级顺序加载
public interface PrioritySPI extends Comparable<Integer> {
    // 获取插件优先级
    SPIIdentify getIdentify();
    @Override
    default int compareTo(Integer o) {
        return Integer.compare(getIdentify().getPriority(), o);
    }
}

// 任务“通道”工厂,创建任务“通道”,任务“通道”可以根据任务类型创建具体的任务
// 任务插件实际上是注册的任务工厂插件,通过任务工厂插件进一步创建任务
public interface TaskChannelFactory extends PrioritySPI {
    TaskChannel create();
    default SPIIdentify getIdentify() {
        return SPIIdentify.builder().name(getName()).build();
    }
}

// 任务“通道”,根据任务类型创建具体的任务
public interface TaskChannel {
    AbstractTask createTask(TaskExecutionContext taskRequest);
}

// 例如HTTP任务插件,分别实现了任务“通道”工厂和任务”通道“,任务“通道”工厂创建具体的任务”通道“,任务”通道“创建具体的任务
// 新增其他任务类型时,也只需要实现这两个接口即可
@AutoService(TaskChannelFactory.class)
public class HttpTaskChannelFactory implements TaskChannelFactory {
    @Override
    public String getName() {
        return "HTTP";
    }
    @Override
    public TaskChannel create() {
        return new HttpTaskChannel();
    }
}

// HTTP任务”通道“,创建HTTP任务
public class HttpTaskChannel implements TaskChannel {
    @Override
    public AbstractTask createTask(TaskExecutionContext taskRequest) {
        return new HttpTask(taskRequest);
    }
}

上面是任务家族的各个核心成员。下面的插件工厂和插件管理器负责任务插件的初始化和管理。


// 插件工厂,初始化时会加载各个任务工厂
public class PrioritySPIFactory<T extends PrioritySPI> {
    private final Map<String, T> map = new HashMap<>();
    public PrioritySPIFactory(Class<T> spiClass) {
        for (T t : ServiceLoader.load(spiClass)) {
            if (map.containsKey(t.getIdentify().getName())) {
                // 根据任务优先级解决同名插件冲突
                resolveConflict(t);
            } else {
                map.put(t.getIdentify().getName(), t);
            }
        }
    }
}


// 任务插件管理器,维护任务插件map,可以根据任务类型获取具体的任务插件
public class TaskPluginManager {
    private final Map<String, TaskChannelFactory> taskChannelFactoryMap = new HashMap<>();
    private final Map<String, TaskChannel> taskChannelMap = new HashMap<>();

    // 初始化插件工厂,会加载各个任务“通道”工厂
    PrioritySPIFactory<TaskChannelFactory> prioritySPIFactory = new PrioritySPIFactory<>(TaskChannelFactory.class);
    // 根据加载的任务“通道”工厂创建具体的任务“通道”
    for (Map.Entry<String, TaskChannelFactory> entry : prioritySPIFactory.getSPIMap().entrySet()) {
        TaskChannelFactory factory = entry.getValue();
        taskChannelMap.put(factoryName, factory.create());
    }
}
  1. AbstractTask 具体任务的抽象类,定义了任务的初始化方法 init,执行方法 handle,取消方法 cancel,各个具体的任务会按需实现各自的逻辑。
public abstract class AbstractTask {
    public void init() {
    }
    public abstract void handle(TaskCallBack taskCallBack) throws TaskException;
    public abstract void cancel() throws TaskException;
}

例如 Http 任务,init 方法初始化任务相关参数,handle 方法发送请求,取消方法什么都不需要做。当然你也可以按需实现自己的 HTTP 任务,在 cancel 方法加些自己的逻辑,比如通知接收方回退等等。

@Override
public void init() {
    this.httpParameters = JSONUtils.parseObject(taskExecutionContext.getTaskParams(), HttpParameters.class);
}

@Override
public void handle(TaskCallBack taskCallBack) throws TaskException {
    sendRequest(client)}

@Override
public void cancel() throws TaskException {
}
对于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、付费专栏及课程。

余额充值