MapReduce詳細的工作流程(MapReduce1)

本文着重介紹MapReduce詳細的工作流程,這些知識可以讓你更深刻的理解Mapreduce,編寫高級的MapReduce項目。

MapReduce Job運行剖析

如果一個Job還沒提交的話,可以通過運行submit()或者waitForCompletion()方法來運行一個MapReduce Job。在Hadoop 0.20及以上的版本中,mapred.job.tracker來確定Job執行的方式。如果這些配置爲local(默認的),則用本地的Job Runner來運行程序,這些runner在一個單獨的JVM中運行整個Job。這種設計是爲了在小數據集上運行MapReduce程序。當然,若mapred.jon.tracker是一個由冒號分開的主機和端口,這個配置被解析爲一個JobTracker地址,本地的runner則會提交job到該地址的JobTracker,具體的內容將會在下面說清楚。
在Hadoop 2.0中,有一種新的MapReduce實現方式,這種實現方式在一個叫YARN的系統是構建起來的。必須注意這個框架是用mapreduce.framwork.name來配置的。
  1. lcoal:本地的job runner;
  2. calssic:也叫MapReduce 1,用jobTracker和taskTracker;
  3. yarn:新的YARN框架

MapReduce 1

一個在MapReduce1 中運行的job的詳細流程如圖6-1所示,從大方面來說,有四個獨立的模塊:

  - The Client 用來提交MapReduce Job;
  - The jobTracker 用來協調Job工作的運行
  - The taskTracker 運行被分割好的任務
  - HDFS 用來在不同的模塊間分享數據

這裏寫圖片描述
Job Submit
首先調用Job的submit()方法會創建一個JobSummitter的實例,同時調用該實例的submitJobInternal()方法。(step1) 提交job之後,如果從上次提交到這次提交之間發生了改變,waitForCompletion()每秒鐘向控制檯提交job的進度。當job成功的完成了,會展示job counter,否則,引起job失敗的錯誤的將會寫到控制檯。JobSummitter操作的job提交過程如下:

  1. 向jobTracker請求一個新的jobID(通過調用JobTracker的getNewJobId()方法)(step2)
  2. 覈對該job的輸出說明。例如,如果輸出結果的文件夾沒有被指定或者已經存在,那麼該Job則不會提交,且拋出一個錯誤。
  3. 計算該Job的輸入分割(input split),如果不能分割(輸入文件的路徑不存在),該job就不會被提交,且拋出一個錯誤。
  4. 複製需要運行該Job的資源到jobTracker的文件系統下的以jobID命名的文件下。包括 job JAR file,配置文件,計算好的輸入分割。job JAR file 可以被快速複製,因此當運行Job的task時,taskracker可以通過集羣訪問這些複製的JAR file。 (step3)
  5. 告訴jobTracker,job已經準備好了,可以調用JobTracker的submitJob()。(step4)

Job初始化

當一個JobTracker收到一個調用它的submitJob() 請求,它將這個請求放入一個內部棧裏面,在這個棧裏面,JobTracker會找到該請求,並初始化。初始化時將會創建一個對象來表示job正在運行,該對象封裝了它的任務,同時記錄了任務的狀態和進度。(step5)
爲了創建一系列要運行的task,job scheduler 首先遍歷HDFS中客戶端計算的輸入分割(input splits),(step6),然後爲每一個分割創建map task,reduce task的數量是由Job中的mapred.reduce.tasks來配置的,可以通過setNumReduceTasks()來設置。同時給予每一個任務一個Id。還要創建兩個task,設置任務(job setup task)和清除任務(job cleanup task).設置任務用來在map任務運行之前設置job,清除任務用來在所有的reduce任務之後清除一些配置和內容。OutputCommitter用來配置job中要運行的代碼,默認的是FileOutputCommitter。對設置任務來說,它將要創建job最終的輸出文件夾和task輸出的臨時文件夾。對清除任務來說,它將要刪除task產生的臨時文件。

任務分配

TaskTracker運行一個簡單的循環向jobTracker週期性的發送心跳。心跳告訴JobTracker TaskTracker是活的,他們也可以把該心跳作爲一個通道來作爲雙方的通信的橋樑。心跳的一部分內容可以用來告訴jobTracker TaskTracker是否要準備運行一個新的任務,是的話,jobTracker就會分配一些任務給這個TaskTracker,這個任務通過心跳返回value來與taskTracker通信。(step7)在爲一個taskTracker選擇一個task之前,jobTracker必須選擇那個task屬於的job,存在各種各樣的算法,默認的是簡單地維持一個job 鏈表,選擇一個job之後,jobtracker對這個job選擇一個task。TaskTracker爲map tasks和reduce tasks提供固定數目的插槽(slot),這些插槽是相互獨立的。舉個例子,一個taskTracker可能配置能夠同時運行兩個map task和reduce task。對於一個給定的job,job scheduler 先在插槽內填充map task,再填充reduce task。

任務執行

現在taskTracker被分配了一個任務,下一步就是執行任務。首先,它會從HDFS中複製 job JAR到taskTracker的文件系統,然後複製其他需要的文件。(step8)。其次,它爲這個任務創建一個本地的工作目錄,同時解壓JAR的內容到這個目錄中。最後,它會爲這個將要運行的任務創建一個TaskRunner實例。TaskRunner構建一個新的JVM**(step9)**來運行每一個任務,因此map和reduce方法的錯誤不影響taskTracker。同時,這樣的話,就有可能去重用JVM。子進程通過中央接口與taskRunner通信,每隔幾秒告訴taskRunner該任務的進度,直到該任務結束。每一個任務都會執行設置(setup)和清除(cleanup)行爲,這兩個行爲和任務運行在一個JVM中。清除行爲用於提交任務,對於一個基於文件的job來說,調用了cleanup就意味着這個任務最後的輸出已經寫入指定的位置。這個提交協議確保當是預測執行(speculative execution)的狀態時,只有一個副本任務被提交,其餘的被終止。

流(stream)和管道(pipes)

流和管道都可運行用戶提供的map和reduce任務。在用流的情況下,流任務(streaming task)用標準的輸入輸出流與進程進行交互。管道任務則是偵聽一個socket同時傳遞給一個C++進程一個能夠啓動端口號,這個C++進程能夠建立一個長連接和它的上層JAVA pipe 任務交互。不論是哪一種情況,java 進程傳遞input key—value 鍵值對到外部進程,這個外部進程運行用戶定義的map或者reduce方法,然後傳遞output key—value 返回給Java 進程。從taskTracker的角度來看,好像是他自己的子進程運行的。如圖 6-2
這裏寫圖片描述

進度和狀態的更新

MapReduce Jobs一般是長時間運行的 job塊,從幾分鐘到幾個小時不等。這是一個很長的時間,因此對用戶來說job的運行反饋非常重要。每一個job和它的任務都有一個身份(status),包括job和task的狀態(成功和失敗)、map和reduce 的進度,job counter的值、一個身份信息或者描述信息。這些狀態會隨着job的運行而更改,但是它是如何與client進行通信的呢?
當一個task運行的時候,它知道自己的進度。對map任務來說,就是input數據被處理的比例。對reduce來說,有點複雜,但是系統能夠預估給reduce的輸入(input)處理了多少。系統根據洗牌(shuffle)的三個階段將整個進度華爲三部分。舉個例子,加入任務已經運行了reducer的input的一半,那麼整個任務的進度已經運行了5/6,因爲它已經完成了複製(copy)和排序(sort)階段(各佔1/3),同時運行reduce階段的一半(1/6)。
如果一個task向上級報告進度,它會設置一個標籤來表示狀態的改變。這個標籤在一個其他的進程中每隔三秒被覈對一次,如果發生了改變,這件進程將會通知taskTracker目前的狀態。同時,taskTracker每隔五秒向jobTracker發送一次心跳,這個心跳包含這個taskTracker運行的所有task的狀態。計數器(counter)被髮送的時間要比5秒長,因爲相對來說需要更多的帶寬。jobTracker合併這些更新狀態併產生一個包含所有正在運行的job及job內任務的全局狀態視圖。最後Job 收到jobTracker每秒鐘提供的狀態。客戶端通過調用Job的getStatus()來獲取一個JobStatus實例,這個實例中包含這個job中所有的實例。如圖6-3:
這裏寫圖片描述
當一個jobTracker收到一個通知最後一個task已經完成了(cleanup task),它改變這個job的狀態爲successful。當The Job詢問狀態的時候,發現這個job已經成功,因此它用waitForCompletion()打印一個信息告訴user,Job統計的信息和counter在控制檯打印。如果你配置job.end.notification.url的話,jobTracker也可能發送一個HTTP job 通知。最後,jobTracker清除它的工作狀態同時告訴taskTracker做同樣的工作(刪除map產生的中間層的output)。

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