2021-11-22---2021SC@SDUSC---DolphinScheduler(9)

2021-11-22
2021SC@SDUSC

DolphinScheduler(9)

1. ShellTask

AbstractTask的子类现在有9个,分析ShellTask,这是一个常见的任务类型。

public ShellTask(TaskProps taskProps, Logger logger) {
    super(taskProps, logger);
    
    this.taskDir = taskProps.getTaskDir();
    
    this.shellCommandExecutor = new ShellCommandExecutor(this::logHandle, taskProps.getTaskDir(),
            taskProps.getTaskAppId(),
            taskProps.getTaskInstId(),
            taskProps.getTenantCode(),
            taskProps.getEnvFile(),
            taskProps.getTaskStartTime(),
            taskProps.getTaskTimeout(),
            logger);
    this.processDao = SpringApplicationContext.getBean(ProcessDao.class);
}
先看其构造函数,有两个字段的初始化比较重要:
shellCommandExecutor、processDao。
ShellCommandExecutor是shell脚本的执行器,具体功能后面再分析。
processDao的初始化方法又是通过SpringApplicationContext.getBean获取到的。
把这些dao等其他类型的全局或局部变量封装到TaskContenxt,如果任务之间传递变量,就可以用TaskContenxt了。
根据init、handle、after的名称来看,具体的执行应该是在handle。
public void handle() throws Exception {
    try {
      // construct process
      exitStatusCode = shellCommandExecutor.run(buildCommand(), processDao);
    } catch (Exception e) {
      logger.error("shell task failure", e);
      exitStatusCode = -1;
    }
    }

handle就是调用shellCommandExecutor.run,如果出现异常,则exitStatusCode赋值-1

shellCommandExecutor.run的代码多,只分析shellCommandExecutor的buildProcess的方法。

buildProcess

private void buildProcess(String commandFile) throws IOException {
    //init process builder
    ProcessBuilder processBuilder = new ProcessBuilder();
    // setting up a working directory
    processBuilder.directory(new File(taskDir));
    // merge error information to standard output stream
    processBuilder.redirectErrorStream(true);
    // setting up user to run commands
    processBuilder.command("sudo", "-u", tenantCode, commandType(), commandFile);

    process = processBuilder.start();

    // print command
    printCommand(processBuilder);
}

它根据commandFile创建了一个ProcessBuilder,返回了Process对象。当然了,是通过sudo -u执行的shell命令。

2.DependentTask

DependentTask虽然是AbstractTask的一个子类,虽然与shell属于同一个层级的类,但由于其功能的特殊性,此处单独拿出来做分析。

public void handle(){
    // set the name of the current thread
    String threadLoggerInfoName = String.format(Constants.TASK_LOG_INFO_FORMAT, taskProps.getTaskAppId());
    Thread.currentThread().setName(threadLoggerInfoName);

    try{
        TaskInstance taskInstance = null;
        while(Stopper.isRunning()){
            taskInstance = processDao.findTaskInstanceById(this.taskProps.getTaskInstId());

            if(taskInstance == null){
                exitStatusCode = -1;
                break;
            }

            if(taskInstance.getState() == ExecutionStatus.KILL){
                this.cancel = true;
            }

            if(this.cancel || allDependentTaskFinish()){
                break;
            }

            Thread.sleep(Constants.SLEEP_TIME_MILLIS);
        }

        if(cancel){
            exitStatusCode = Constants.EXIT_CODE_KILL;
        }else{
            DependResult result = getTaskDependResult();
            exitStatusCode = (result == DependResult.SUCCESS) ?
                    Constants.EXIT_CODE_SUCCESS : Constants.EXIT_CODE_FAILURE;
        }
    }catch (Exception e){
        logger.error(e.getMessage(),e);
        exitStatusCode = -1;
    }
}

逻辑总结如下:

    1.通过任务实例id,获取当前最新的任务实例信息
    2.判断状态是否为kill,是则退出
    3.判断所有依赖任务是否完成,是则退出
    4.休眠1秒,进入下一次循环。


allDependentTaskFinish

allDependentTaskFinish是一个非常重要的逻辑。

private boolean allDependentTaskFinish(){
    boolean finish = true;
    for(DependentExecute dependentExecute : dependentTaskList){
        for(Map.Entry<String, DependResult> entry: dependentExecute.getDependResultMap().entrySet()) {
            if(!dependResultMap.containsKey(entry.getKey())){
                dependResultMap.put(entry.getKey(), entry.getValue());
                //save depend result to log
                logger.info("dependent item complete {} {},{}",
                        DEPENDENT_SPLIT, entry.getKey(), entry.getValue().toString());
            }
        }
        if(!dependentExecute.finish(dependentDate)){
            finish = false;
        }
    }
    return finish;
}

它遍历了dependentTaskList,通过dependentExecute.finish(dependentDate)判断了依赖的作业是否全部完成,任意一个没有完成,则退出循环,返回false。

dependentDate的值也很重要,它其实是任务的调度时间或者启动时间(补数时间)


if(taskProps.getScheduleTime() != null){
    this.dependentDate = taskProps.getScheduleTime();
}else{
    this.dependentDate = taskProps.getTaskStartTime();
}

calculateResultForTasks

通过一层层追踪分析DependentExecute.finish,我们定位到了DependentExecute.calculateResultForTasks,这是用来判断某个依赖项的依赖结果的。

/**
 * calculate dependent result for one dependent item.
 * @param dependentItem dependent item
 * @param dateIntervals date intervals
 * @return dateIntervals
 */
private DependResult calculateResultForTasks(DependentItem dependentItem,
                                                    List<DateInterval> dateIntervals) {
    DependResult result = DependResult.FAILED;
    for(DateInterval dateInterval : dateIntervals){
        ProcessInstance processInstance = findLastProcessInterval(dependentItem.getDefinitionId(),
                                                dateInterval);
        if(processInstance == null){
            logger.error("cannot find the right process instance: definition id:{}, start:{}, end:{}",
                   dependentItem.getDefinitionId(), dateInterval.getStartTime(), dateInterval.getEndTime() );
            return DependResult.FAILED;
        }
        if(dependentItem.getDepTasks().equals(Constants.DEPENDENT_ALL)){
            result = getDependResultByState(processInstance.getState());
        }else{
            TaskInstance taskInstance = null;
            List<TaskInstance> taskInstanceList = processDao.findValidTaskListByProcessId(processInstance.getId());

            for(TaskInstance task : taskInstanceList){
                if(task.getName().equals(dependentItem.getDepTasks())){
                    taskInstance = task;
                    break;
                }
            }
            if(taskInstance == null){
                // cannot find task in the process instance
                // maybe because process instance is running or failed.
                 result = getDependResultByState(processInstance.getState());
            }else{
                result = getDependResultByState(taskInstance.getState());
            }
        }
        if(result != DependResult.SUCCESS){
            break;
        }
    }
    return result;
}

总结:如果依赖整个DAG,则判断流程定义实例的状态;
否则依次判断依赖任务实例的状态。

DependentTask的逻辑简单清晰,就是循环等待所有的任务结束。DependentTask可以单独设计成一个线程,或者放到独立的线程池去运行。对于一个调度系统来说,“依赖”还是一个非常重要的概念。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

奋斗的仓鼠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值