Job的任务执行流程之JobSetup阶段

      客户端用户在成功提交Job之后,该Job并不会马上被JobTracker节点调度执行,因为这个Job还没有被初始化,至于JobTracker节点是如何初始化Job的,我在前面已经详细的谈到过。当一个提交的Job在被初始化之后,就可以正式地被JobTracker节点调度执行了,不过,JobTracker节点不会就此开始调度Job的Map/Reduce任务,而是调用该Job的SetupTask。因此,本文将重点讲解一个Job执行的Setup阶段,在这个过程中我主要会以Job及其Task的状态转移为主线来详细的展开。

    Job作业被用户成功提交之后就处于PREP状态但此时JobTracker节点并不会就此马上开始调度这个作业的Map/Reduce任务,而是先调度这个Job的SetupTask任务(请注意这个Task的类型是Map),很明显JobTracker会为这个TaskInProgress创建一个任务实例MapTask,此时这个MapTask的状态是UNASSIGNED,之后JobTracker节点就会将这个MapTask交给一个合适的TaskTracker节点来执行。这个TaskTracker节点收到这个MapTask实例之后,就会为被包装成一个本地TaskInProgress,该TaskInProgress的状态和MapTask实例的状态一样是UNASSIGNED。之后这个本地TaskInProgress就会等待TaskTracker节点上的一个空闲的Map Slot来执行。当这个本地TaskInProgressTaskTracker节点调度并本地初始化之后,这个TaskInProgress的状态就变成了RUNNING。不过,在MapTask到达TaskTracker节点之后,TaskTracker节点会通过心跳包不断地向JobTracker节点汇报这个MapTask对应的状态信息。


        Map/Reduce任务在同一时刻可能有多个对应的实例在不同的TaskTracker节点上运行,但是Job的SetupTask任务在同一时刻只能有一个实例在TaskTracker节点上运行,也就是说如果一个Job的SetupTask任务实例被分配到某一个TaskTracker节点上运行的话,那么JobTracker节点就不能再为这个Job的SetupTask任务创建一个实例并交给其它的TaskTracker节点运行,除非JobTracker节点已经明确地知道了那个SetupTask任务实例已经执行失败了。如果SetupTask的任务实例被成功执行之后JobInProgress的状态就变为RUNNING。TaskTracker节点在向JobTracker节点报告其上的所有任务实例的运行时状态之后,就会清楚那些处于SUCCEEDEDFAILDKILLED状态的任务实例,即它不会再向JobTracker节点报告这些已经结束的任务实例。JobTracker节点在收到一个任务实例的状态报告之后会更新这个任务实例所属的任务TaskInProgress的状态信息,这个状态更新处理的源代码如下:

//返回的结果是这个任务的状态有没有发生变化
synchronized boolean updateStatus(TaskStatus status) {
    TaskAttemptID taskid = status.getTaskID();
    String diagInfo = status.getDiagnosticInfo();
    TaskStatus oldStatus = taskStatuses.get(taskid);//这个任务实例上一次的状态
    boolean changed = true;
    
    if (diagInfo != null && diagInfo.length() > 0) {
      LOG.info("Error from "+taskid+": "+diagInfo);
      addDiagnosticInfo(taskid, diagInfo);
    }
    
    if(skipping) {
      failedRanges.updateState(status);
    }
    
    if (oldStatus != null) {
      TaskStatus.State oldState = oldStatus.getRunState();
      TaskStatus.State newState = status.getRunState();
          
      // 已经完成的任务实例(success/failure/killed)不可能被TaskTracker节点报告两次,
      // 但为了安全起见还是先检查一下
      if ((newState != TaskStatus.State.RUNNING && newState != TaskStatus.State.COMMIT_PENDING && newState != TaskStatus.State.FAILED_UNCLEAN &&  newState != TaskStatus.State.KILLED_UNCLEAN &&  newState != TaskStatus.State.UNASSIGNED) && (oldState == newState)) {
        LOG.warn("Recieved duplicate status update of '" + newState +  "' for '" + taskid + "' of TIP '" + getTIPId() + "'");
        return false;
      }

      // 任务实例的状态不肯能往回转移,但为了安全起见还是要检查一下.
      if ((newState == TaskStatus.State.RUNNING || 
          newState == TaskStatus.State.UNASSIGNED) &&
          (oldState == TaskStatus.State.FAILED || 
           oldState == TaskStatus.State.KILLED || 
           oldState == TaskStatus.State.FAILED_UNCLEAN || 
           oldState == TaskStatus.State.KILLED_UNCLEAN || 
           oldState == TaskStatus.State.SUCCEEDED ||
           oldState == TaskStatus.State.COMMIT_PENDING)) {
        return false;
      }
      
      //任务的实例状态已经是FAILED/KILLED,就不会再更新了
      if (oldState == TaskStatus.State.FAILED || oldState == TaskStatus.State.KILLED) {
        tasksToKill.put(taskid, true);
        return false;	  
      }
          
      changed = oldState != newState;
    }

    // 更新任务实例的状态.
    if (!isCleanupAttempt(taskid)) {
      taskStatuses.put(taskid, status);
    } else {
      taskStatuses.get(taskid).statusUpdate(status.getRunState(), status.getProgress(), status.getStateString(), status.getPhase(), status.getFinishTime());
    }

    //重新计算任务的当前进度
    recomputeProgress();
    return changed;
  }

      JobTracker节点并没有限制一个作业的最大执行时间,这就是说,如果一个Job的一个Task在有限的时间内无法执行完或者说无法完成的话,JobTracker节点并没有相应的机制来自动的kill掉这个作业,这个作业将会一直的跑下去,知道提交者自己手动强制结束掉这个任务。另外,Job的SetupTask任务的主要任务就是为该Job做一些准备工作,如申请最后的输出数据的存储空间等,它的实现就是调用OutputCommitter的setupJob()方法,这个OutputCommitter来源于该Job配置的输出格式化器OutputFormat,不过在此之前,它还必须调用OutputCommitter的setupTask()方法,这是因为任何一个Task实例在任何一个JVM中执行真正的任务之前都必须调用OutputCommittersetupTask()方法来启动任务,尽管有些OutputCommitter的实现中setupTask()方法没有做任何事情,但这个方法可以看做是Task执行过程中的一个钩子方法以便解决用户的特殊问题。值得一提的是,目前的Map-Reduce执行框架对于一个JobSetup任务实例,并不保证在这个任务实例执行失败时,一定会调用对应的OutputCommitter的abortTask()方法,所以,如果用户想自己实现一个特定作业的OutputCommitter的时候,一定要自己来保证setupTask()方法的事务一致性。同时,这也告诉我们,对于一般的Map/Reduce任务,我们可以通过OutputCommittersetupTask()和abortTask()来保证任务实例执行时的一致性(主要是原子性)。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值