2021-10-18---2021SC@SDUSC---DolphinScheduler(4)

2021-10-18
2021SC@SDUSC—DolphinScheduler(4)

1. Stopper.isRunning()

在分析run之前,首先我们先分析一下该方法的内在的逻辑

/**
 *  if the process closes, a signal is placed as true, and all threads get this flag to stop working
 */
public class Stopper {

	private static volatile AtomicBoolean signal = new AtomicBoolean(false);
	
	public static final boolean isStoped(){
		return signal.get();
	}
	
	public static final boolean isRunning(){
		return !signal.get();
	}
	
	public static final void stop(){
		signal.getAndSet(true);
	}
}

其逻辑非常简单,就是用一个原子boolean(布尔值),标志当前进程是否要退出。
如果收到了退出信号,则signal为true,该进程内所有的线程都退出当前循环。

下面我们来分析查询到一个Command之后的逻辑:

if (command != null) {
    logger.info(String.format("find one command: id: %d, type: %s", command.getId(),command.getCommandType().toString()));

    try{
        processInstance = processDao.handleCommand(logger, OSUtils.getHost(), this.masterExecThreadNum - activeCount, command);
        if (processInstance != null) {
            logger.info("start master exec thread , split DAG ...");
            masterExecService.execute(new MasterExecThread(processInstance,processDao));
        }
    }catch (Exception e){
        logger.error("scan command error ", e);
        processDao.moveToErrorCommand(command, e.toString());
    }
}

其实就是根据Command创建了一个ProcessInstance(流程实例),
之前也分析过,流程定义是由Scheduler自动创建的,
而Quartz已经根据Schedule信息创建了Command保存到了数据库。
至此,流程定义与定时的关联逻辑就已经串起来了。

创建流程实例的时候传入了当前可用(masterExecThreadNum - activeCount)的线程数量,
如果满足当前dag,则返回ProcessInstance,否则返回null。

ProcessInstance最终交由MasterExecThread去执行。

至此MasterSchedulerThread类的主要逻辑如下:
    调用OSUtils.checkResource,检查当前资源(内存、CPU)。
    资源超出阈值,则休眠1秒进入下一次循环。
    检查zookeeper是否连接成功
    获取一个InterProcessMutex锁(分布式的公平可重入互斥锁)。
    也就是只有一个master可以获取到这个锁
    查询一个Command,
    如果当前线程数够用,则创建一个流程实例(ProcessInstance)
    交给MasterExecThread线程处理。
    休眠1秒,进入下一次循环
    进入下一次循环之前,释放InterProcessMutex锁

在结束MasterExecThread的源码分析之前,我们再简要分析一下这个类比较重要的一个字段:

processDao请添加图片描述

这个类,可以看成是与流程定义相关的操作集合,
与流程定义存储相关的操作、逻辑的集合。
processDao.moveToErrorCommand需要稍微注意一下,
在异常情况下,它把Command从原来的表中删除,
然后插入到了t_ds_error_command表1

MasterExecThread

与MasterSchedulerThread一样,MasterExecThread也是实现了Runnable的线程类,不过我们先来看MasterExecThread的构造函数。


public MasterExecThread(ProcessInstance processInstance,ProcessDao processDao){
    this.processDao = processDao;

    this.processInstance = processInstance;

    int masterTaskExecNum = conf.getInt(Constants.MASTER_EXEC_TASK_THREADS,
            Constants.defaultMasterTaskExecNum);
    this.taskExecService = ThreadUtils.newDaemonFixedThreadExecutor("Master-Task-Exec-Thread",
            masterTaskExecNum);
}

taskExecService这个字段非常重要,它是一个固定大小(20)的后台线程池。
这意味着,一个DAG最大的并发任务数就是20。

另外细心的读者发现,conf字段是一个static字段,在static代码块初始化的。
为啥不从MasterSchedulerThread传过来呢?
我还以为要自动reload呢,结果也没有。


static {
    try {
        conf = new PropertiesConfiguration(Constants.MASTER_PROPERTIES_PATH);
    }catch (ConfigurationException e){
        logger.error("load configuration failed : " + e.getMessage(),e);
        System.exit(1);
    }
}

2

下面分析该类的run方法


@Override
public void run() {

    // process instance is null
    if (processInstance == null){
        logger.info("process instance is not exists");
        return;
    }

    // check to see if it's done
    if (processInstance.getState().typeIsFinished()){
        logger.info("process instance is done : {}",processInstance.getId());
        return;
    }

    try {
        if (processInstance.isComplementData() &&  Flag.NO == processInstance.getIsSubProcess()){
            // sub process complement data
            executeComplementProcess();
        }else{
            // execute flow
            executeProcess();
        }
    }catch (Exception e){
        logger.error("master exec thread exception: " + e.getMessage(), e);
        logger.error("process execute failed, process id:{}", processInstance.getId());
        processInstance.setState(ExecutionStatus.FAILURE);
        processInstance.setEndTime(new Date());
        processDao.updateProcessInstance(processInstance);
    }finally {
        taskExecService.shutdown();
        // post handle
        postHandle();
    }
}

分析源码后,简要总结其逻辑如下:

    判断processInstance是否为null。为null则退出
    判断processInstance是否已经完成(成功、报错、取消、暂停、等待)
    判断是否为补数。是则走补数的逻辑
    执行当前流程定义实例(executeProcess)
    调用taskExecService.shutdown(),等待所有线程正常退出

3

executeProcess按顺序调用了prepareProcess、runProcess、endProcess三个方法,简单来说就是初始化、执行、释放资源。 prepareProcess又按顺序调用了initTaskQueue、buildFlowDag。

initTaskQueue就是一些资源的初始化操作,比如通过流程定义ID查询到当前的任务实例。下面是其核心逻辑,可以发现,就是查询了完成的任务列表,报错且不能重试的任务列表。


List<TaskInstance> taskInstanceList = processDao.findValidTaskListByProcessId(processInstance.getId());
for(TaskInstance task : taskInstanceList){
    if(task.isTaskComplete()){
        completeTaskList.put(task.getName(), task);
    }
    if(task.getState().typeIsFailure() && !task.taskCanRetry()){
        errorTaskList.put(task.getName(), task);
    }
}

buildFlowDag看名字应该是生成DAG实例的,代码虽短,但调用了好几个函数,我们只重点分析最后一个函数调用。


private void buildFlowDag() throws Exception {
    recoverNodeIdList = getStartTaskInstanceList(processInstance.getCommandParam());

    forbiddenTaskList = DagHelper.getForbiddenTaskNodeMaps(processInstance.getProcessInstanceJson());
    // generate process to get DAG info
    List<String> recoveryNameList = getRecoveryNodeNameList();
    List<String> startNodeNameList = parseStartNodeName(processInstance.getCommandParam());
    ProcessDag processDag = generateFlowDag(processInstance.getProcessInstanceJson(),
            startNodeNameList, recoveryNameList, processInstance.getTaskDependType());
    if(processDag == null){
        logger.error("processDag is null");
        return;
    }
    // generate process dag
    dag = DagHelper.buildDagGraph(processDag);
}

DagHelper.buildDagGraph生成了一个DAG对象实例,根据名字和注释猜测,这应该是对有向无环图的一个抽象。


/**
 * the object of DAG
 */
private DAG<String,TaskNode,TaskNodeRelation> dag;


  1. 但个人感觉其定义不是非常清晰。如果是mapper的一个全集,则其他任何地方都不应该再调用mapper,事实又不是这样;如果只是流程定义相关的操作, 其功能又过于大。 ↩︎

  2. 配置这种字段,完全可以全局唯一,到处传参,没必要在new一个。一般情况下这个类的内容也不会修改。 ↩︎

  3. 感觉第一步有点多此一举 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的仓鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值