Job的任務執行流程之TaskCleanup

      任何一個作業在Hadoop集羣中執行主要包括四個階段:setup、map、reduce、cleanup,但在這四個階段都出現任務實例在TaskTracker節點執行失敗的情況。當一個任務實例在TaskTracker節點的JVM中執行時除了成功執行意外,還有可能出現一些異常情況:1).在JVM中執行失敗;2).JVM進程被操作系統stop;3).任務實例被JobTracker節點要求kill;這些異常情況都會造成該任務實例執行的失敗,從而使得該任務進入FAILEDFAILED_UNCLEANKILLED_UNCLEAN等三種狀態中的某一種。這裏就有一個問題了,一個任務實例失敗時到底會進入哪一種狀態?這其實很好判斷:

1).如果一個任務實例在JVM中運行時出現異常或錯誤而無法再繼續運行,同時在調用了該任務所屬作業對應的OutputCimmitter輸出提交器的abortTask()方法之後離開JVM的話,這個任務實例會進入FAILED狀態;

2).如果一個任務實例在JVM中運行時出現異常或錯誤而無法再繼續運行,同時在沒有調用該任務所屬作業對應的OutputCimmitter輸出提交器的abortTask()方法就離開了JVM的話,這個任務實例會進入FAILED_UNCLEAN狀態;

3).如果一個任務實例在JVM中正常運行時突然被停止了(如:任務實例所在的JVM進程被OSstop或者被TaskTracker節點強制命令停止),此時還來不起調用該任務所屬作業對應的輸出提交器OutputCimmitter的abortTask()方法,所以它會進入KILLED_UNCLEAN狀態。

本文將主要圍繞JobTracker節點對處於FAILED_UNCLEANKILLED_UNCLEAN狀態的任務實例的處理來詳細地展開講解。

    TaskTracker在任務實例停止執行之後,就會把這個任務實例對應的狀態報告給JobTracker節點來處理,當然,前面說過,JobTracker節點是不會直接處理任何任務實例的狀態報告的,而是交給對應的JobInProgress來處理。對於處於FAILED_UNCLEANKILLED_UNCLEAN狀態的任務實例,JobInProgress會將他們存儲在對應的待清理的任務隊列中,當然,一個作業主要包含兩種這樣的任務隊列,一種存儲Map型的任務實例,另一種存儲Reduce型的任務實例,然後它會交給合適的TaskTracker節點來執行對該任務的清理操作。這種清理工作就是前面所說的TaskCleanup任務。這個處理過程是是很簡單的,對應的源代碼如下:

class JobInProgress {

...

   public synchronized void updateTaskStatus(TaskInProgress tip, TaskStatus status) {

      ...
     if (state == TaskStatus.State.FAILED_UNCLEAN || state == TaskStatus.State.KILLED_UNCLEAN) {
        tip.incompleteSubTask(taskid, this.status);
        // add this task, to be rescheduled as cleanup attempt
        if (tip.isMapTask()) {
            mapCleanupTasks.add(taskid);
        } else {
          reduceCleanupTasks.add(taskid);
        }
        // Remove the task entry from jobtracker
        jobtracker.removeTaskEntry(taskid);
      }
     ...
  } 

...

}

    上一篇博文也說過,當一個作業中有TaskCleanup任務的話,就會優先調度這些TaskCleanup任務,而不會調度它的正式Map/Reduce任務。對應的調度策略也很簡單,源碼如下:

public Task obtainTaskCleanupTask(TaskTrackerStatus tts, boolean isMapSlot) throws IOException {
    if (!tasksInited.get()) {
      return null;
    }
    
    synchronized (this) {
      if (this.status.getRunState() != JobStatus.RUNNING || jobFailed || jobKilled) {
        return null;
      }
      
      String taskTracker = tts.getTrackerName();
      if (!shouldRunOnTaskTracker(taskTracker)) {
        return null;
      }
      
      TaskAttemptID taskid = null;
      TaskInProgress tip = null;
      if (isMapSlot) {
        if (!mapCleanupTasks.isEmpty()) {
          taskid = mapCleanupTasks.remove(0);
          tip = maps[taskid.getTaskID().getId()];
        }
      } else {
        if (!reduceCleanupTasks.isEmpty()) {
          taskid = reduceCleanupTasks.remove(0);
          tip = reduces[taskid.getTaskID().getId()];
        }
      }
      
      if (tip != null) {
        return tip.addRunningTask(taskid, taskTracker, true);
      }
      
      return null;
    }
    
  }
    TaskTracker節點對TaskCleanup任務的本地化和調度同JobSetup、JobCleanup、Map、Reduce任務是一樣,最終都會交給一個JVM實例來負責執行。在JVM中,它主要會調用作業對應的輸出提交器OutputCimmitter的abortTask()方法,即放棄該任務,在FileOutputCimmitter實現中就是清理該任務實例在執行過程中所佔用的臨時存儲空間。這裏要提醒的是,無論這個TaskCleanup任務在JVM中執行成功或者失敗,或者在本地化時就出錯而被kill掉,它都會進入對應的FAILED或者KILLED狀態:如果該TaskCleanup任務處於FAILED_UNCLEAN狀態,它就會進入FAILED狀態;和如果該TaskCleanup處於KILLED_UNCLEAN狀態,它就會進入KILLED狀態。TaskCleanup任務被TaskTracker節點執行完之後的處理同JobSetup、JobCleanup、Map、Reduce任務實例也是一樣的,所以就不再贅述了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章