TaskTracker節點的內部設計與實現

      衆所周知,Hadoop採用master-slave架構設計來實現Map-Reduce框架,它的JobTracker節點作爲主控節點來管理和調度用戶提交的作業,TaskTracker節點作爲工作節點來負責執行JobTracker節點分配的Map/Reduce任務。整個集羣由一個JobTracker節點和若干個TaskTracker節點組成,當然,JobTracker節點也負責對TaskTracker節點進行管理。在前面一系列的博文中,我已經比較系統地講述了JobTracker節點內部的設計與實現,而在本文,我將對TaskTracker節點的內部設計與實現進行一次全面的概述。

      TaskTracker節點作爲工作節點不僅要和JobTracker節點進行頻繁的交互來獲取作業的任務並負責在本地執行他們,而且也要和其它的TaskTracker節點交互來協同完成同一個作業。因此,在目前的Hadoop-0.20.2.0實現版本中,對工作節點TaskTracker的設計主要包含三類組件:服務組件、管理組件、工作組件。服務組件不僅負責與其它的TaskTracker節點而且還負責與JobTracker節點之間的通信服務,管理組件負責對該節點上的任務、作業、JVM實例以及內存進行管理,工作組件則負責調度Map/Reduce任務的執行。這三大組件的詳細構成如下:


下面來詳細的介紹這三類組件


服務組件

     TaskTracker節點內部的服務組件不僅用來爲TaskTracker節點、客戶端提供服務,而且還負責向TaskTracker節點請求服務,這一類組件主要包括HttpServer、TaskReportServer、JobClient三大組件。

1.HttpServer

     TaskTracker節點在其內部使用Jetty Web容器來開啓http服務,這個http服務一是用來爲客戶端提供Task日誌查詢服務,二是用來提供數據傳輸服務,即在執行Reduce任務時是通過TaskTracker節點提供的該http服務來獲取屬於自己的map輸出數據。這裏需要詳細介紹的是與該服務相關的配置參數,集羣管理者可以通過TaskTracker節點的配置文件來配置該服務地址和端口號,對應的配置項爲:mapred.task.tracker.http.address。同時,爲了能夠靈活的控制該該服務的吞吐量,管理者還可以設置該http服務的內部工作線程數量,對應的配置爲:tasktracker.http.threads

2.Task Reporter

       TaskTracker節點在接收到JobTracker節點發送過來的Map/Reduce任務之後,會把它們交給JVM實例來執行,而自己則需要收集這些任務的執行進度信息,這就使得Task在JVM實例中執行的時候需要不斷地向TaskTracker節點報告當前的執行情況。雖然TaskTracker節點和JVM實例在同一臺機器上,但是它們之間的進程通信卻是通過網絡I/O來完成的(此處並不討論這種通信方式的性能),也就是TaskTracker節點在其內部開啓一個端口來專門爲任務實例提供進度報告服務。該服務地址可以通過配置項mapred.task.tracker.report.address來設置,而服務內部的工作線程的數量取2倍於該TaskTracker節點上的Map/Reduce Slot數量中的大者。該服務接口如下:

interface TaskUmbilicalProtocol extends VersionedProtocol {

  public static final long versionID = 16L;
  
  /**
   *  從TaskTracker節點上獲取一個屬於該JVM實例的任務
   */
  JvmTask getTask(JVMId jvmId) throws IOException;

  /**
   * 向TaskTracker節點報告當前任務實例執行的進度及狀態信息
   */
  boolean statusUpdate(TaskAttemptID taskId, TaskStatus taskStatus) 
  throws IOException, InterruptedException;
  
  /** 
   * 向TaskTracker節點報告當前任務實例發生異常的原因
   */
  void reportDiagnosticInfo(TaskAttemptID taskid, String trace) throws IOException;
  
  /**
   * 
   */
  void reportNextRecordRange(TaskAttemptID taskid, SortedRanges.Range range) 
    throws IOException;

  /** 
   */
  boolean ping(TaskAttemptID taskid) throws IOException;

  /** 
   * 向TaskTracker節點發送一個心跳包,以此來告知TaskTracker節點自己還在正常執行
   */
  void done(TaskAttemptID taskid) throws IOException;
  
  /** 
   * 向TaskTracker節點發送任務提交預約請求
   */
  void commitPending(TaskAttemptID taskId, TaskStatus taskStatus) 
  throws IOException, InterruptedException;  

  /**
   * 向TaskTracker節點詢問自己是否能夠提交任務
   */
  boolean canCommit(TaskAttemptID taskid) throws IOException;

  /** 
   * 向TaskTracker節點報告獲取一個Map輸出失敗
   */
  void shuffleError(TaskAttemptID taskId, String message) throws IOException;
  
  /** 
   * 向TaskTracker節點報告執行一個任務實例是發送文件系統的錯誤 
   */
  void fsError(TaskAttemptID taskId, String message) throws IOException;

  /** 
   * 向TaskTracker節點報告一個任務實例執行失敗
   */
  void fatalError(TaskAttemptID taskId, String message) throws IOException;
  
  /** 
   * 從TaskTracker節點獲取所屬作業剛剛完成的Map任務
   */
  MapTaskCompletionEventsUpdate getMapCompletionEvents(JobID jobId, 
                                                       int fromIndex, 
                                                       int maxLocs,
                                                       TaskAttemptID id) 
  throws IOException;

}
3.JobClient

    TaskTracker節點與JobTracker節點進行通信的客戶端組件,該組件負責爲TaskTracker節點提供與系統有關的查詢服務,該客戶端的API爲:

interface InterTrackerProtocol extends VersionedProtocol {
  
  public static final long versionID = 25L;
  
  public final static int TRACKERS_OK = 0;
  public final static int UNKNOWN_TASKTRACKER = 1;

  /**
   * 向JobTracker節點發送心跳跑同時報告自己當前的狀態及任務實例執行的進度信息等,並且順便獲取分配的新任務實例
   */
  HeartbeatResponse heartbeat(TaskTrackerStatus status, 
                              boolean restarted, 
                              boolean initialContact,
                              boolean acceptNewTasks,
                              short responseId)
    throws IOException;
  
  /**
   * 獲取集羣依賴的文件系統的類型
   */
  public String getFilesystemName() throws IOException;

  /**
   * 向JobTracker節點報告TaskTracker節點出錯的原因
   */
  public void reportTaskTrackerError(String taskTracker,
                                     String errorClass,
                                     String errorMessage) throws IOException;
  /**
   * 從JobTracker節點獲取某一作業最近的任務完成事件
   */
  TaskCompletionEvent[] getTaskCompletionEvents(JobID jobid, int fromEventId
      , int maxEvents) throws IOException;

  /**
   * 獲取集羣的系統目錄
   */
  public String getSystemDir();
  
  
  /**
   * 從JobTracker節點獲取其提供該服務的版本號
   */
  public String getBuildVersion() throws IOException;
}


管理組件

1.JVM Manager

    TaskTracker節點對任務實例執行的核心就是將其交由一個的合適的JVM進程來負責執行,這裏要強調的是JVM實例和TaskTracker節點是在同一臺機器上的兩個不同的進程,因此運行在每一個TaskTracker節點上的JVM實例是有限制的,同時又兩類JVM實例,一類用來運行Map任務,另一類則用來運行Reduce任務。這兩類JVM實例的最大數量可分別由配置文件中的mapred.tasktracker.map.tasks.maximummapred.tasktracker.reduce.task.maximum項來設置。TaskTracker節點利用JvmManager來管理該節點上所有的JVM實例,一方面它要保證運行在該TaskTracker節點上的各類JVM實例數量不能超過對應的上限值;另一方面還要負責分配合適的任務實例到空閒的JVM上執行,這是因爲頻繁地關閉/開始JVM線程是很耗時間的,而且一個JVM實例只能運行同一個作業的Map/Reduce任務。

2.Memory Manager

   Hadoop集羣的成本低主要是因爲它部署在普通的低端PC上,以至於集羣中的每一個節點的物理資源是有限的,特別是內存資源。任何作業的任務在TaskTracker節點上執行時都或多或少地需要一定的內部,但不同類型的作業需要的內存量是不一樣的。很明顯,僅僅通過開啓一個JVM實例時設置該進程可使用的內存的上限值是不準確的、不夠優化的,所以TaskTracker節點通過TaskMemoryManager來統計該節點當前內存的使用情況,並將其報告給JobTracker節點,JobTracker節點就可以根據該TaskTracker節點內存的剩餘爲其分配合適的任務了。

3.Task/Job Manager

   TaskTracker節點通過Task、Job兩個級別來共同的管理正在其上運行的任務,本來在Task級別管理任務是足以的,但爲什麼還要在Job級別再設置一層管理呢?這是因爲,在前面剛介紹給,TaskTracker節點對JVM實例的調度級別是Job一級的,而JVM實例執行的單位是Task級別。


工作組件

1.MapEvent Fetcher

   MapEvent Fetcher組件是TaskTracker節點內部的一個後臺工作線程,用來向JobTracker節點請求某一個作業的任務完成事件,這個作業是正在TaskTracker節點上執行的。這個組件存在的意義在於Reduce任務在執行的時候需要Map任務的輸出數據,因此Reduce任務在執行reduce操作之前需要shuffle所有的map輸出到本地。

2.Map/Reduce Launcher

   Map/Reduce Launcher組件也是TaskTracker節點內部的兩個後臺工作線程,它們分別負責調度合適的Map/Reduce任務到對應空閒/新的JVM實例上執行。當然,在將Task交給JVM實例運行之前還負責該任務對應的作業即自身的本地化。

3.TaskCleanup

    TaskTracker節點每一次向JobTracker節點發送心跳包之後,除了帶回一些需要JVM實例執行的任務之外,還帶回來了一些操作命令,如KillJob、KillTask等,這一類任務並不需要JVM實例來執行,因此,TaskTracker節點在其內部開啓了一個後臺線程來專門的處理這些由於某些原因需要kill掉在正在其上執行的Job和Task。Job或Task的kill操作主要包括關閉對應的JVM實例,清理它們在TaskTracker節點上佔用的臨時Disk/Mem存儲空間。對於在TaskTracker節點上執行的Job/Task所佔用的臨時磁盤存儲空間的清理,也是由一個後臺線程來全權負責處理的,這樣做一是因爲文件或目錄的刪除操作可能比較消耗時間,二是這些文件或目錄已經沒有任何用處了而不需要急着清除。

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