JobTracker節點對Task實例狀態報告的處理

     前面談到過,每一個TaskTracker節點都要在它向JobTracker節點發送心跳包的時候順帶報告運行在其上的Task的狀態信息,這些Task是指正在TaskTracker節點上運行的,或者從上一次報告到現在的時間間隔中完成的或者是失敗的任務。JobTracker節點之所以需要收集這些正在執行或者剛完成的任務的狀態信息,是因爲它要及時掌握各個作業的執行進度,一方面將此報告給用戶,一方面還調整作業的任務調度,如任務的意外處理,任務/作業的善後處理等。這裏說的任務的意外處理包括,對於失敗的任務或者是拖後退的任務需要從新安排其它的TaskTracker節點來執行等,詳細情況我會在後面再作介紹。

      對於JobTracker節點處理TaskTracker發送過來的Task狀態報告的詳細過程,我將結合源代碼來闡述。但在此之前,我想先介紹一下相關的幾個概念:作業/作業實例、任務/任務實例。作業指的是用戶提交的作業,用Job的實例對象來表示,作業實例指的是Job運行的狀態信息,用JobInProgress的實例對象來表示;任務指的是該任務的執行進度狀態信息,用TaskInProgress的實例對象來表示,任務實例則指的是在將該任務交由某一個TaskTacker節點執行的任務,用TaskAttempID的實例對象來表示。一個作業對應一個作業實例,而一個任務可能對應多個任務實例,這是因爲一個任務可能交給了多個TaskTracker節點來執行。JobTracker節點處理Task實例狀態報告的源碼如下:

void updateTaskStatuses(TaskTrackerStatus status) {
    String trackerName = status.getTrackerName();
    
    for (TaskStatus report : status.getTaskReports()) {
      report.setTaskTracker(trackerName);
      TaskAttemptID taskId = report.getTaskID();
      
      // expire it
      expireLaunchingTasks.removeTask(taskId);
      
      JobInProgress job = getJob(taskId.getJobID());
      if (job == null) {
        // if job is not there in the cleanup list ... add it
        synchronized (trackerToJobsToCleanup) {
          Set<JobID> jobs = trackerToJobsToCleanup.get(trackerName);
          if (jobs == null) {
            jobs = new HashSet<JobID>();
            trackerToJobsToCleanup.put(trackerName, jobs);
          }
          jobs.add(taskId.getJobID());
        }
        continue;
      }
      
      if (!job.inited()) {
        // if job is not yet initialized ... kill the attempt
        synchronized (trackerToTasksToCleanup) {
          Set<TaskAttemptID> tasks = trackerToTasksToCleanup.get(trackerName);
          if (tasks == null) {
            tasks = new HashSet<TaskAttemptID>();
            trackerToTasksToCleanup.put(trackerName, tasks);
          }
          tasks.add(taskId);
        }
        continue;
      }

      TaskInProgress tip = taskidToTIPMap.get(taskId);
      // Check if the tip is known to the jobtracker. In case of a restarted
      // jt, some tasks might join in later
      if (tip != null || hasRestarted()) {
        if (tip == null) {
          tip = job.getTaskInProgress(taskId.getTaskID());
          job.addRunningTaskToTIP(tip, taskId, status, false);
        }
        
        // Update the job and inform the listeners if necessary
        JobStatus prevStatus = (JobStatus)job.getStatus().clone();
        // Clone TaskStatus object here, because JobInProgress
        // or TaskInProgress can modify this object and
        // the changes should not get reflected in TaskTrackerStatus.
        // An old TaskTrackerStatus is used later in countMapTasks, etc.
        job.updateTaskStatus(tip, (TaskStatus)report.clone());
        JobStatus newStatus = (JobStatus)job.getStatus().clone();
        
        // Update the listeners if an incomplete job completes
        if (prevStatus.getRunState() != newStatus.getRunState()) {
          JobStatusChangeEvent event = new JobStatusChangeEvent(job, EventType.RUN_STATE_CHANGED, prevStatus, newStatus);
          updateJobInProgressListeners(event);
        }
      } else {
        LOG.info("Serious problem.  While updating status, cannot find taskid " + report.getTaskID());
      }
      
      // Process 'failed fetch' notifications 
      List<TaskAttemptID> failedFetchMaps = report.getFetchFailedMaps();
      if (failedFetchMaps != null) {
        for (TaskAttemptID mapTaskId : failedFetchMaps) {
          TaskInProgress failedFetchMap = taskidToTIPMap.get(mapTaskId);
          
          if (failedFetchMap != null) {
            // Gather information about the map which has to be failed, if need be
            String failedFetchTrackerName = getAssignedTracker(mapTaskId);
            if (failedFetchTrackerName == null) {
              failedFetchTrackerName = "Lost task tracker";
            }
            failedFetchMap.getJob().fetchFailureNotification(failedFetchMap, mapTaskId, failedFetchTrackerName);
          }
        }
      }
    }
    
  }
     從上面的源代碼可以看出,JobTracker節點對於每一個Task狀態報告的處理考慮到了一些意外情況,這些意外情況主要包括:

     1).Task實例所屬的作業不在JobTracker節點的任務隊列內。出現這個情況的可能原因是這個Task實例是一個拖後退的任務,當它完成時,該Task實例所屬的Job早已被完成了,或者該Job被用戶kill掉了。對於這種情況,JobTracker節點就直接讓該TaskTracker把運行在其上的所有屬於該Job的Task全部清除(這個TaskTracker節點上可能還運行有該Job的拖後腿任務)。

     2).Task實例所屬的作業還沒有初始化(以前說過,沒有被初始化的Job是不能被JobTracker節點調度執行的)。出現這種情況的一個可能的原因是TaskTracker節點剛剛重啓過,在恢復作業的時候還沒有來得及初始化該Job。對於這種情況,JobTracker節點就直接讓該TaskTracker清除該任務。

   3).Task實例所屬的任務還沒有被JobTracker節點調度執行。出現這種情況的一個可能的原因是JobTracker節點在該Task實例執行期間重啓了,重啓之後還沒有來得及調度該任務。對於這種情況,JobTracker節點爲了提高性能就認爲該情況是還算是正常的,不過得先得通知該Task實例所屬的Job實例。

    4).對於Reduce Task實例的進度狀態報告,它還會附帶額外的報告信息,就是報告那些它無法fetch到map輸出的Map任務,JobTracker節點需要將這些消息轉告給該Task實例所屬的作業實例,以便該Job實例能夠及時調整其內部任務的調度。至於Job實例是如何處理的,並不是本文所要介紹的重點,不過我會在以後的博文中詳細闡述。

     在正常的情況下,JobTracker節點不會直接的處理這些Task實例的狀態報告,而是將其交給它們所屬的Job實例來處理Job實例處理之後如果它的狀態發生變化,JobTracker節點會通知對應的事件監聽器。那麼,Job實例又是如何根據它的一個Task實例狀態來更新自己當前的狀態呢?由於這個過程相當的複雜不適合於此時介紹。




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