標籤:ffffff 大小 while instance 產生 lis tail 並行 etc
MapReduce架構的優勢是能夠在叢集中並行運行mapper和reducer任務,那怎樣確定mapper和reducer的數量呢,或者說怎樣以編程的方式控製作業啟動的mapper和reducer數量呢?在《Hadoop-2.4.1學習之Mapper和Reducer》中以前提及建議reducer的數量為(0.95~1.75 ) * 節點數量 * 每一個節點上最大的容器數,並可用法Job.setNumReduceTasks(int)。mapper的數量由輸入檔案的大小確定。且沒有相應的setNumMapTasks方法,但能夠通過Configuration.set(JobContext.NUM_MAPS, int)設定,當中JobContext.NUM_MAPS的值為mapreduce.job.maps,而在Hadoop的官方網站上對該參數的描寫敘述為與MapReduce架構和作業配置巧妙地互動。而且設定起來更加複雜。
從這樣一句含糊不清的話無法得知到底怎樣確定mapper的數量。顯然僅僅能求助於源碼了。
在Hadoop中MapReduce作業通過JobSubmitter類的submitJobInternal(Jobjob, Cluster cluster)方法向系統提交作業(該方法不僅設定mapper數量。還運行了一些其他動作如檢查輸出格式等,感興趣的能夠參考源碼),在該方法中與設定mapper有關的代碼例如以下:
int maps = writeSplits(job, submitJobDir);conf.setInt(MRJobConfig.NUM_MAPS, maps);LOG.info("number of splits:" + maps);
方法writeSplits返回mapper的數量,該方法的源碼例如以下:
private int writeSplits(org.apache.hadoop.mapreduce.JobContext job,Path jobSubmitDir) throws IOException,InterruptedException, ClassNotFoundException { JobConf jConf = (JobConf)job.getConfiguration(); int maps; if (jConf.getUseNewMapper()) { maps = writeNewSplits(job, jobSubmitDir); } else { maps = writeOldSplits(jConf, jobSubmitDir); } return maps; }
在該方法中,依據是否使用了新版本號碼的JobContext而使用不同的方法計算mapper數量。實際情況是jConf.getUseNewMapper()將返回true,因此將運行writeNewSplits(job,jobSubmitDir)語句,該方法的源碼例如以下:
Configuration conf = job.getConfiguration();InputFormat<?, ?> input = ReflectionUtils.newInstance(job.getInputFormatClass(), conf);List<InputSplit> splits = input.getSplits(job);T[] array = (T[]) splits.toArray(new InputSplit[splits.size()]);// sort the splits into order based on size, so that the biggest// go firstArrays.sort(array, new SplitComparator());JobSplitWriter.createSplitFiles(jobSubmitDir, conf, jobSubmitDir.getFileSystem(conf), array);return array.length;
通過上面的代碼能夠得知,實際的mapper數量為輸入分區的數量,而分區的數量又由使用的輸入格式決定,默覺得TextInputFormat,該類為FileInputFormat的子類。確定分區數量的任務交由FileInputFormat的getSplits(job)完畢,在此補充一下FileInputFormat繼承自抽象類別InputFormat,該類定義了MapReduce作業的輸入規範,當中的抽象方法List<InputSplit> getSplits(JobContext context)定義了怎樣將輸入切割為InputSplit。不同的輸入有不同的分隔邏輯,而分隔得到的每一個InputSplit交由不同的mapper處理,因此該方法的傳回值確定了mapper的數量。以下將分為兩部分學習該方法是怎樣在FileInputFormat中實現的,為了將注意力集中在最重要的部分。對日誌輸出等資訊將不做介紹,完整的實現能夠參考源碼。
首先是第一部分,該部分代碼計算了最大InputSplit和最小InputSplit的值,例如以下:
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));long maxSize = getMaxSplitSize(job);
當中的getMinSplitSize和getMaxSplitSize方法分別用於擷取最小InputSplit和最大InputSplit的值。相應的配置參數分別為mapreduce.input.fileinputformat.split.minsize。預設值為1L和mapreduce.input.fileinputformat.split.maxsize,預設值為Long.MAX_VALUE,十六進位數值為 0x7fffffffffffffffL,相應的十進位為9223372036854775807,getFormatMinSplitSize方法返回該輸入格式下InputSplit的下限。
以上數位單位都是byte。由此得出minSize的大小為1L。maxSize的大小為Long.MAX_VALUE。
其次是產生InputSplit的第二部分。在該部分將產生包括InputSplit的List,而List的大小為InputSplit的數量,進而確定了mapper的數量。當中重要的代碼為:
if (isSplitable(job, path)) { long blockSize = file.getBlockSize(); long splitSize = computeSplitSize(blockSize, minSize, maxSize); long bytesRemaining = length; while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) { int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining); splits.add(makeSplit(path, length-bytesRemaining, splitSize, blkLocations[blkIndex].getHosts())); bytesRemaining -= splitSize; } if (bytesRemaining != 0) { int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining); splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining, blkLocations[blkIndex].getHosts())); }}
blockSize的值為參數dfs.blocksize的值,默覺得128M。方法computeSplitSize(blockSize, minSize, maxSize)依據blockSize,minSize。maxSize確定InputSplit的大小,源碼例如以下:
Math.max(minSize, Math.min(maxSize, blockSize))
從該代碼並結合第一部分的分析能夠得知,InputSplit的大小取決於dfs.blocksiz、mapreduce.input.fileinputformat.split.minsize、mapreduce.input.fileinputformat.split.maxsize和所使用的輸入格式。
在輸入格式為TextInputFormat的情況下,且不改動InputSplit的最大值和最小值的情況,InputSplit的終於值為dfs.blocksize的值。
變數SPLIT_SLOP的值為1.1。決定了當剩餘檔案大小多大時停止依照變數splitSize切割檔案。
依據代碼可知,當剩餘檔案小於等於1.1倍splitSize時,將把剩餘的檔案做為一個InputSplit。即最後一個InputSplit的大小最大為1.1倍splitSize。
總結 本文分析了在輸入格式為預設的TextInputFormat的情況,怎樣確定mapper的數量。在不改動源碼的情況下(改動輸入格式的InputSplit下限)。程式猿能夠通過設定dfs.blocksiz、mapreduce.input.fileinputformat.split.minsize、mapreduce.input.fileinputformat.split.maxsize參數的值設定InputSplit的大小來影響InputSplit的數量。進而決定mapper的數量。
當輸入為其他格式時,處理邏輯又不同樣了,比方當輸入格式為DBInputFormat時。會依據輸入表的行數(記錄數)決定mapper的數量。很多其他細節能夠參考源碼。
Hadoop-2.4.1學習之怎樣確定Mapper數量