Hadoop中Mapper和Reducer是單獨進程還是線程

hadoop一個節點默認起兩個map slot,請問這兩個slot是多線程嗎?
hadoop-0.21.0 源碼中是這樣的:
首先看看 org.apache.hadoop.mapred.TaskTracker 類:
兩個類變量 maxMapSlots 和 maxReduceSlots:
maxMapSlots = conf.getInt(TT_MAP_SLOTS, 2);
maxReduceSlots = conf.getInt(TT_REDUCE_SLOTS, 2);
其中 public static final String TT_MAP_SLOTS = "mapreduce.tasktracker.map.tasks.maximum";
public static final String TT_REDUCE_SLOTS = "mapreduce.tasktracker.reduce.tasks.maximum";
類方法 initializeMemoryManagement() 中 ,根據 slots 來決定申請內存的大小
totalMemoryAllottedForTasks = maxMapSlots * mapSlotMemorySizeOnTT + maxReduceSlots * reduceSlotSizeMemoryOnTT;
類方法 TaskTracker.initialize() 中會起兩個 TaskLauncher 線程,
分別負責啓動 Mapper 和 Reduce 任務:
mapLauncher = new TaskLauncher(TaskType.MAP, maxMapSlots); 
reduceLauncher = new TaskLauncher(TaskType.REDUCE, maxReduceSlots);
再看看 org.apache.hadoop.mapred.TaskTracker.TaskLauncher 類,它負責啓動 Mapper/Reducer 任務。
初始化 TaskLauncher 時,需要傳入 slots 的數量:
public TaskLauncher(TaskType taskType, int numSlots) {
    this.maxSlots = numSlots;
    this.numFreeSlots = new IntWritable(numSlots);
    }
特別要注意 numFreeSlots 這個類變量:
private IntWritable numFreeSlots; 
TaskLauncher.run() 中,循環地看是否有新的 Task 需要啓動,並且看是否有足夠的 slots 可用:
while () {
    while (numFreeSlots.get() < task.getNumSlotsRequired()) { .......}
numFreeSlots.set(numFreeSlots.get() - task.getNumSlotsRequired()); // 用完了就減掉
}
Task 執行完了以後,需要釋放 slots :
public void addFreeSlots(int numSlots) {... ...  
numFreeSlots.set(numFreeSlots.get() + numSlots);
... ... }
所以,綜合上面看,slots 只是一個邏輯值 ( org.apache.hadoop.mapred.TaskTracker.TaskLauncher.numFreeSlots ),而不是對應着一個線程或者進程。TaskLauncher 會維護這個值,以保證資源使用在控制範圍內。幫助理解的最主要的代碼可見 : org.apache.hadoop.mapred.TaskTracker.TaskLauncher.run() 。
Mapper 和 Reducer 都是單獨的進程,但是它們與 slots 的關係是這樣的:
org.apache.hadoop.mapred.TaskTracker.TaskLauncher.run() {... ... //got a free slot. launch the task  startNewTask(tip); ... ...}這裏的 slots 有點類似 “令牌” 的感覺:申請資源,先獲得令牌;釋放資源,交還令牌。
mapper 和 reducer 都是單獨的進程?好像有點不對,是單獨的線程吧?是單獨的進程。
啓動Mapper/Reducer的總的調用路徑是:
org.apache.hadoop.mapred.TaskTracker.TaskLauncher.run()->org.apache.hadoop.mapred.TaskTracker.startNewTask()->org.apache.hadoop.mapred.TaskTracker.launchTaskForJob()->org.apache.hadoop.mapred.TaskTracker.TaskInProgress.launchTask()?->org.apache.hadoop.mapred.Task.createRunner() // 抽象方法,具體實現在子類 MapTask 和 ReduceTask 中 
org.apache.hadoop.mapred.MapTask.createRunner() // 創建 MapTaskRunner 類實例 
org.apache.hadoop.mapred.ReduceTask.createRunner() // 創建 ReduceTaskRunner 類實例
最終,跟蹤到了 MapTaskRunner 和 ReduceTaskRunner 這兩個類。
至此,我們看看它們的父類 org.apache.hadoop.mapred.TaskRunner ,以下是類的說明:
/** Base class that runs a task in a separate process. Tasks are run in a  separate process in order to isolate the map/reduce system code from bugs in  user supplied map and reduce functions.?*/
TaskRunner 雖然 extends Thread (看起來是個線程),但是真正啓動Mapper和Reduce進程的代碼在函數 TaskRunner.run() 中:
public final void run() {... ...
   launchJvmAndWait(setup, vargs, stdout, stderr, logSize, workDir, env);... ...
   }
其調用了 TaskRunner.launchJvmAndWait() 方法(在此之前還有些創建文件夾、設置配置參數和環境變量等準備性的操作):
void launchJvmAndWait(List<String> setup, Vector<String> vargs, File stdout,File stderr, long logSize, File workDir, Map<String, String> env)throws InterruptedException {jvmManager.launchJvm(this, jvmManager.constructJvmEnv(setup, vargs, stdout,stderr, logSize, workDir, env, conf));synchronized (lock) {while (!done) {lock.wait();}}}
上面代碼主要是 launch 一個 java虛擬機進程。這也是Hadoop啓動代價很高的原因,因爲launch虛擬機是比較耗資源的;於是又提供了Task JVM Reuse機制。
單獨起進程的原因也說得很清楚,就是: isolate the map/reduce system code from bugs in user supplied map and reduce functions。其實就是,通過使用不同的進程空間,進行隔離,防止用戶提供的代碼中有bug死掉後,造成 TaskTracker 所在進程也死掉(這個死掉了,效果就跟阿凡達裏面的發光樹被毀了一樣)。
Hadoop-0.20.2源碼中的實現基本也是差不多的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章