datax(9):Job和TaskGroup的通讯机制

datax(9):Job和TaskGroup的通讯机制先后看完了TaskGroupContainer和JobContainer,梳理下他们的关系与职责;一,各自职责JobContainer:Job执行器,负责Job全局拆分、调度、前置语句和后置语句等工作的工作单元。类似Yarn中的JobTrackerTaskGroupContainer:TaskGroup执行器,负责执行一组Task的工作单元,类似Yarn中的TaskTracker(Yarn中的JobTracker和Yarn中的TaskTracker通过RPC进行通讯);二.

大家好,又见面了,我是你们的朋友全栈君。

先后看完了TaskGroupContainer 和 JobContainer,梳理下他们的关系与职责;


一、各自职责

  1. JobContainer: Job执行器,负责Job全局拆分、调度、前置语句和后置语句等工作的工作单元。类似Yarn中的JobTracker

  2. TaskGroupContainer: TaskGroup执行器,负责执行一组Task的工作单元,类似Yarn中的TaskTracker(Yarn中的JobTracker和Yarn中的TaskTracker通过RPC进行通讯);


二、相互关系

  1. TaskGroupContainer向JobContainer上报信息,称JobContainer是TaskGroupContainer的上级;
  2. Task向TaskGroupContainer上报信息,称TaskGroupContainer是Task的上级。

在这里插入图片描述


三、TaskGroupContainer向JobContianer汇报过程

汇报源码逻辑是在TaskGroupContainer#reportTaskGroupCommunication这个方法中,方法的两个形参分别为lastTaskGroupContainerCommunication为上次汇报的信息,每次做数据统计的时候需要将当前communication的数据和lastTaskGroupContainerCommunication进行合并;taskCount为该TaskGroup的所有的任务数。

  1. 收集当前TaskGroupContainer对应所有Task的的communication,然后将其合并成一个communication。
    具体合并代码为步骤1,主要逻辑是在Communication#mergeFrom,它的主要功能将两communication的变量合并。主要关注下两个communication的状态合并,可以看到只要该TaskGroup中有一个Task的状态是FAILED或者KILLED就会将整个TaskGroup的状态标记为FAILED,当且仅当所有的任务的状态是SUCCEEDED,该TaskGroup的状态才能标记为SUCCEEDED。

  2. 生成新的reportCommunication作为该TaskGroupContainer上报给JobContianer的communication,
    主要是生成一些技术统计,比方说当前已经导入的记录数和字节数等。

  3. 上报给JobContianer,主要代码见步骤2,将该TaskGroupContainer最新的communication更新到StandAloneJobContainerCommunicator 能够够的得到的地方,即全局变量LocalTGCommunicationManager#taskGroupCommunicationMap中。


  private Communication reportTaskGroupCommunication(Communication lastTGContainerComm,int taskCnt) { 
   
    Communication nowTGContainerComm = this.containerCommunicator.collect();
    nowTGContainerComm.setTimestamp(System.currentTimeMillis());
    Communication reportComm = CommunicationTool
        .getReportCommunication(nowTGContainerComm, lastTGContainerComm, taskCnt);
    this.containerCommunicator.report(reportComm);
    return reportComm;
  }

步骤1


//AbstractCollector#collectFromTask
public Communication collectFromTask() { 
   
    Communication communication = new Communication();
    communication.setState(State.SUCCEEDED);

    for (Communication taskCommunication :
            this.taskCommunicationMap.values()) { 
   
    communication.mergeFrom(taskCommunication);
    }
    return communication;
}
Communication#mergeStateFrom
public synchronized State mergeStateFrom(final Communication otherComm) { 
   
        State retState = this.getState();
        if (otherComm == null) { 
   
            return retState;
        }

        if (this.state == State.FAILED || otherComm.getState() == State.FAILED
                || this.state == State.KILLED || otherComm.getState() == State.KILLED) { 
   
            retState = State.FAILED;
        } else if (this.state.isRunning() || otherComm.state.isRunning()) { 
   
            retState = State.RUNNING;
        }

        this.setState(retState);
        return retState;
}

步骤2


// StandaloneTGContainerCommunicator#report
public void report(Communication communication) { 
   
    super.getReporter().reportTGCommunication(super.taskGroupId, communication);
}

// ProcessInnerReporter
public void reportTGCommunication(Integer taskGroupId, Communication communication) { 
   
    LocalTGCommunicationManager.updateTaskGroupCommunication(taskGroupId, communication);
}

// LocalTGCommunicationManager#updateTaskGroupCommunication
public static void updateTaskGroupCommunication(final int taskGroupId,
                                                    final Communication communication) { 
   
        Validate.isTrue(taskGroupCommunicationMap.containsKey(
                taskGroupId), String.format("taskGroupCommunicationMap中没有注册taskGroupId[%d]的Communication," +
                "无法更新该taskGroup的信息", taskGroupId));
        taskGroupCommunicationMap.put(taskGroupId, communication);
}


四、TaskGroupContainer向JobContianer汇报时机

TaskGroupContainer向JobContainer汇报该TaskGroupContainer的执行情况的时机均在TaskGroupContainer#start中。

1、当前TaskGroup中有状态为FAILED或者KILLED的Task

如果一个Task只能执行一次(默认是1次,没有做重试)且该Task被标记为FAILED或者KILLED,马上将failedOrKilled这个变量标记为true并执行汇报逻辑。这种情况下除了汇报之后,还会抛出一个运行时异常,结束执行当前TaskGroupContainer的线程(TaskGroupContianer是在线程池中执行的)。


if (failedOrKilled) { 
   
    lastTaskGroupContainerCommunication = reportTaskGroupCommunication(
            lastTaskGroupContainerCommunication, taskCountInThisTaskGroup);

    throw DataXException.asDataXException(
        FrameworkErrorCode.PLUGIN_RUNTIME_ERROR, lastTaskGroupContainerCommunication.getThrowable());
}

2、上次失败的Task仍未结束

如果一个Task标记为FAILED或者KILLED,但是有重试逻辑就不会执行上面第1步的逻辑,而是会调用当前的Task对应TaskExecutor#shutdown,关闭当前的TaskExecutor。在调用TaskExecutor#shutdown一段时间发发现给TaskExecutor还没有关闭,触发下面逻辑,进行汇报的同时抛出异常。

if(now - failedTime > taskMaxWaitInMsec){ 
   
    markCommunicationFailed(taskId);
    reportTaskGroupCommunication(lastTaskGroupContainerCommunication, taskCountInThisTaskGroup);
    throw DataXException.asDataXException(CommonErrorCode.WAIT_TIME_EXCEED, "task failover等待超时");
}

3、TaskGroupContainer任务列表为空,所有任务都是成功执行, 搜集状态为SUCCEEDED

这个没什么好说的,该TaskGroup中所有的任务执行成功,该Job执行成功。

4、如果当前时间已经超出汇报时间的interval,那么我们需要马上汇报

可以理解为心跳了

5、TaskGroupContainer所在的线程正常结束时汇报一次

这个真没什么好说的了


五、JobContainer收到汇报之后的处理

JobContainer的处理逻辑是在dataX所在JVM的主线程中,具体是在AbstractScheduler#schedule中。

  1. 每隔一段时间,合并所有TaskGoupContianer汇报的信息,具体合并的逻辑和TaskGoupContianer合并Task的汇报信息差不多;

  2. 正常结束就正常退出;

  3. 处理isJobKilling,StandAloneScheduler并没有提供kill接口,咱不管;

  4. 重点关注下FAILED的逻辑,直接关闭当前Scheduler的线程池并在主线程中抛出异常,整个dataX进程退出。

// AbstractScheduler#schedule
public void schedule(List<Configuration> configurations) { 
   
        ...
        ...
        Communication lastJobContainerCommunication = new Communication();

        long lastReportTimeStamp = System.currentTimeMillis();
        try { 
   
            while (true) { 
   
                Communication nowJobContainerCommunication = this.containerCommunicator.collect();
                nowJobContainerCommunication.setTimestamp(System.currentTimeMillis());
                LOG.debug(nowJobContainerCommunication.toString());

                //汇报周期
                long now = System.currentTimeMillis();
                if (now - lastReportTimeStamp > jobReportIntervalInMillSec) { 
   
                    Communication reportCommunication = CommunicationTool
                            .getReportCommunication(nowJobContainerCommunication, lastJobContainerCommunication, totalTasks);

                    this.containerCommunicator.report(reportCommunication);
                    lastReportTimeStamp = now;
                    lastJobContainerCommunication = nowJobContainerCommunication;
                }

                errorLimit.checkRecordLimit(nowJobContainerCommunication);

                if (nowJobContainerCommunication.getState() == State.SUCCEEDED) { 
   
                    LOG.info("Scheduler accomplished all tasks.");
                    break;
                }

                if (isJobKilling(this.getJobId())) { 
   
                    dealKillingStat(this.containerCommunicator, totalTasks);
                } else if (nowJobContainerCommunication.getState() == State.FAILED) { 
   
                    dealFailedStat(this.containerCommunicator, nowJobContainerCommunication.getThrowable());
                }

                Thread.sleep(jobSleepIntervalInMillSec);
            }
        } catch (InterruptedException e) { 
   
            // 以 failed 状态退出
            LOG.error("捕获到InterruptedException异常!", e);

            throw DataXException.asDataXException(
                    FrameworkErrorCode.RUNTIME_ERROR, e);
        }

    }
    
    // ProcessInnerScheduler#dealFailedStat
    public void dealFailedStat(AbstractContainerCommunicator frameworkCollector, Throwable throwable) { 
   
        this.taskGroupContainerExecutorService.shutdownNow();
        throw DataXException.asDataXException(
                FrameworkErrorCode.PLUGIN_RUNTIME_ERROR, throwable);
    }


注:

  1. 对源码进行略微改动,主要修改为 1 阿里代码规约扫描出来的,2 clean code;

  2. 所有代码都已经上传到github(master分支和dev),可以免费白嫖

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/145686.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月16日 下午8:00
下一篇 2022年5月16日 下午8:00


相关推荐

  • PHP经纬度距离计算「建议收藏」

    PHP经纬度距离计算「建议收藏」//根据两点经纬度计算距离//纬度1、纬度2、经度1、经度2functionget_two_point_distance($lat1,$lat2,$lng1,$lng2){$radLat1=deg2rad($lat1);//deg2rad()函数将角度转换为弧度$radLat2=deg2rad($lat2);…

    2026年2月23日
    3
  • 漫画:独立女性真的更讨人喜欢吗?

    今天给大家推荐一篇反套路的漫画 谁说女主都要是清纯白莲花? 《总裁想静静》 漫画已经完结,看起来更过瘾! 第一话 第二话 第三话 原文始发于微信公众号(全栈程序员社区):漫画:独立…

    2021年6月22日
    71
  • 智谱AI 的API接入教程:最全RESTful接口调用指南(含Python示例代码)

    智谱AI 的API接入教程:最全RESTful接口调用指南(含Python示例代码)

    2026年3月12日
    2
  • Django(15)外键和表关系[通俗易懂]

    Django(15)外键和表关系[通俗易懂]外键删除操作如果一个模型使用了外键。那么在对方那个模型被删掉后,该进行什么样的操作。可以通过on_delete来指定。可以指定的类型如下:CASCADE:级联操作。如果外键对应的那条数据被删除了,

    2022年7月28日
    6
  • staruml如何使用(一览)

    需求工程课里面老师让我们使用一个软件进行图形绘制:StarUML(简称SU),是一种创建UML类图,生成类图和其他类型的统一建模语言(UML)图表的工具。StarUML是一个开源项目之一发展快、灵活、可扩展性强(zj).StarUML官方下载地址:http://staruml.io/download安装教程:安装步骤很简单,省略。注意:付费软件,如需破解网上很多教程,不破解不影响使用安装之后的主界面简介如图所示:2.创建工程在启动starUML时,系统会默认帮我们创建一个工程如果这个

    2022年4月12日
    227
  • 一元线性回归-最小二乘法推导过程

    一元线性回归-最小二乘法推导过程设一元线性回归方程为,数据样本点为,要想使这n个样本点落在一元线性回归方程附近,不妨设误差为,使得没一个样本点落在一元线性回归方程上,因此有恒成立,所以回归直线应满足的条件是:实际值与回归估计值之间的误差平方和最小,即:此时令,原问题就转换成求解二元函数极小值问题,分别对求偏导:令上两式等于零,即最终求出两个数值,一元线性回归方程也就拟合出来了。…

    2022年5月17日
    64

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号