轉自:http://www.cnblogs.com/yangsy0915/p/5559969.html
市面上的hadoop權威指南一類的都是老版本的書籍了,索性學習并翻譯了下最新版的Hadoop:The Definitive Guide, 4th Edition與大家共同學習。
我們通過提交jar包,進行MapReduce處理,那么整個運行過程分為五個環(huán)節(jié):
1、向client端提交MapReduce job.
2、隨后yarn的ResourceManager進行資源的分配.
3、由NodeManager進行加載與監(jiān)控containers.
4、通過applicationMaster與ResourceManager進行資源的申請及狀態(tài)的交互,由NodeManagers進行MapReduce運行時job的管理.
5、通過hdfs進行job配置文件、jar包的各節(jié)點分發(fā)。
Job 提交過程
job的提交通過調用submit()方法創(chuàng)建一個JobSubmitter實例,并調用submitJobInternal()方法。整個job的運行過程如下:
1、向ResourceManager申請application ID,此ID為該MapReduce的jobId。
2、檢查output的路徑是否正確,是否已經被創(chuàng)建。
3、計算input的splits。
4、拷貝運行job 需要的jar包、配置文件以及計算input的split 到各個節(jié)點。
5、在ResourceManager中調用submitAppliction()方法,執(zhí)行job
Job 初始化過程
1、當resourceManager收到了submitApplication()方法的調用通知后,scheduler開始分配container,隨之ResouceManager發(fā)送applicationMaster進程,告知每個nodeManager管理器。
2、由applicationMaster決定如何運行tasks,如果job數據量比較小,applicationMaster便選擇將tasks運行在一個JVM中。那么如何判別這個job是大是小呢?當一個job的mappers數量小于10個,只有一個reducer或者讀取的文件大小要小于一個HDFS block時,(可通過修改配置項mapreduce.job.ubertask.maxmaps,mapreduce.job.ubertask.maxreduces以及mapreduce.job.ubertask.maxbytes 進行調整)
3、在運行tasks之前,applicationMaster將會調用setupJob()方法,隨之創(chuàng)建output的輸出路徑(這就能夠解釋,不管你的mapreduce一開始是否報錯,輸出路徑都會創(chuàng)建)
Task 任務分配
1、接下來applicationMaster向ResourceManager請求containers用于執(zhí)行map與reduce的tasks(step 8),這里map task的優(yōu)先級要高于reduce task,當所有的map tasks結束后,隨之進行sort(這里是shuffle過程后面再說),最后進行reduce task的開始。(這里有一點,當map tasks執(zhí)行了百分之5%的時候,將會請求reduce,具體下面再總結)
2、運行tasks的是需要消耗內存與CPU資源的,默認情況下,map和reduce的task資源分配為1024MB與一個核,(可修改運行的最小與最大參數配置,mapreduce.map.memory.mb,mapreduce.reduce.memory.mb,mapreduce.map.cpu.vcores,mapreduce.reduce.reduce.cpu.vcores.)
Task 任務執(zhí)行
1、這時一個task已經被ResourceManager分配到一個container中,由applicationMaster告知nodemanager啟動container,這個task將會被一個主函數為YarnChild的java application運行,但在運行task之前,首先定位task需要的jar包、配置文件以及加載在緩存中的文件。
2、YarnChild運行于一個專屬的JVM中,所以任何一個map或reduce任務出現問題,都不會影響整個nodemanager的crash或者hang。
3、每個task都可以在相同的JVM task中完成,隨之將完成的處理數據寫入臨時文件中。
Mapreduce數據流
運行進度與狀態(tài)更新
1、MapReduce是一個較長運行時間的批處理過程,可以是一小時、幾小時甚至幾天,那么Job的運行狀態(tài)監(jiān)控就非常重要。每個job以及每個task都有一個包含job(running,successfully completed,failed)的狀態(tài),以及value的計數器,狀態(tài)信息及描述信息(描述信息一般都是在代碼中加的打印信息),那么,這些信息是如何與客戶端進行通信的呢?
2、當一個task開始執(zhí)行,它將會保持運行記錄,記錄task完成的比例,對于map的任務,將會記錄其運行的百分比,對于reduce來說可能復雜點,但系統(tǒng)依舊會估計reduce的完成比例。當一個map或reduce任務執(zhí)行時,子進程會持續(xù)每三秒鐘與applicationMaster進行交互。
Job 完成
最終,applicationMaster會收到一個job完成的通知,隨后改變job的狀態(tài)為successful。最終,applicationMaster與task containers被清空。
Shuffle與Sort
從map到reduce的過程,被稱之為shuffle過程,MapReduce使到reduce的數據一定是經過key的排序的,那么shuffle是如何運作的呢?
當map任務將數據output時,不僅僅是將結果輸出到磁盤,它是將其寫入內存緩沖區(qū)域,并進行一些預分類。
1、The Map Side
首先map任務的output過程是一個環(huán)狀的內存緩沖區(qū),緩沖區(qū)的大小默認為100MB(可通過修改配置項mpareduce.task.io.sort.mb進行修改),當寫入內存的大小到達一定比例,默認為80%(可通過mapreduce.map.sort.spill.percent配置項修改),便開始寫入磁盤。
在寫入磁盤之前,線程將會指定數據寫入與reduce相應的patitions中,最終傳送給reduce.在每個partition中,后臺線程將會在內存中進行Key的排序,(如果代碼中有combiner方法,則會在output時就進行sort排序,這里,如果只有少于3個寫入磁盤的文件,combiner將會在outputfile前啟動,如果只有一個或兩個,那么將不會調用)
這里將map輸出的結果進行壓縮會大大減少磁盤IO與網絡傳輸的開銷(配置參數mapreduce.map .output.compress 設置為true,如果使用第三方壓縮jar,可通過mapreduce.map.output.compress.codec進行設置)
隨后這些paritions輸出文件將會通過HTTP發(fā)送至reducers,傳送的最大啟動線程通過mapreduce.shuffle.max.threads進行配置。
2、The Reduce Side
首先上面每個節(jié)點的map都將結果寫入了本地磁盤中,現在reduce需要將map的結果通過集群拉取過來,這里要注意的是,需要等到所有map任務結束后,reduce才會對map的結果進行拷貝,由于reduce函數有少數幾個復制線程,以至于它可以同時拉取多個map的輸出結果。默認的為5個線程(可通過修改配置mapreduce.reduce.shuffle.parallelcopies來修改其個數)
這里有個問題,那么reducers怎么知道從哪些機器拉取數據呢?
當所有map的任務結束后,applicationMaster通過心跳機制(heartbeat mechanism),由它知道m(xù)apping的輸出結果與機器host,所以reducer會定時的通過一個線程訪問applicationmaster請求map的輸出結果。
Map的結果將會被拷貝到reduce task的JVM的內存中(內存大小可在mapreduce.reduce.shuffle.input.buffer.percent中設置)如果不夠用,則會寫入磁盤。當內存緩沖區(qū)的大小到達一定比例時(可通過mapreduce.reduce.shuffle.merge.percent設置)或map的輸出結果文件過多時(可通過配置mapreduce.reduce.merge.inmen.threshold),將會除法合并(merged)隨之寫入磁盤。
這時要注意,所有的map結果這時都是被壓縮過的,需要先在內存中進行解壓縮,以便后續(xù)合并它們。(合并最終文件的數量可通過mapreduce.task.io.sort.factor進行配置) 最終reduce進行運算進行輸出。
參考文獻:《Hadoop:The Definitive Guide, 4th Edition》