mapreduce程序中讀取文件過程詳解

hadoop的inputformat包括他的子類reader是maptask讀取數據的重要步驟

一、獲得splits-mapper數

1. jobclinet的submitJobInternal,生成split,獲取mapper數量

 

Java代碼  收藏代碼
  1. public   
  2.   RunningJob submitJobInternal {  
  3.     return ugi.doAs(new PrivilegedExceptionAction<RunningJob>() {  
  4. ....  
  5. int maps = writeSplits(context, submitJobDir);//<span style="font-family: Helvetica, Tahoma, Arial, sans-serif; white-space: normal; background-color: #ffffff;">生成split,獲取mapper數量</span>  
  6. ....  
  7. }}  
 jobclinet的writesplit方法

 

 

Java代碼  收藏代碼
  1. private int writeSplits(org.apache.hadoop.mapreduce.JobContext job,  
  2.       Path jobSubmitDir) throws IOException,  
  3.       InterruptedException, ClassNotFoundException {  
  4.     JobConf jConf = (JobConf)job.getConfiguration();  
  5.     int maps;  
  6.     if (jConf.getUseNewMapper()) {  
  7.       maps = writeNewSplits(job, jobSubmitDir);//新api調用此方法  
  8.     } else {  
  9.       maps = writeOldSplits(jConf, jobSubmitDir);  
  10.     }  
  11.     return maps;  
  12.   }  
  2.writeNewSplits新api方法,反射inputformat類,調用getsplit方法,獲取split數據,並排序,並返回mapper數量
Java代碼  收藏代碼
  1. private <T extends InputSplit>  
  2.   int writeNewSplits(JobContext job, Path jobSubmitDir) throws IOException,  
  3.       InterruptedException, ClassNotFoundException {  
  4.     Configuration conf = job.getConfiguration();  
  5.     InputFormat<?, ?> input =  
  6.       ReflectionUtils.newInstance(job.getInputFormatClass(), conf);//反射到inputsplit  
  7.   
  8.     List<InputSplit> splits = input.getSplits(job);//調用inputformat子類實現的getsplits方法  
  9.     T[] array = (T[]) splits.toArray(new InputSplit[splits.size()]);//生成數組,這麼簡單的方法寫的這麼複雜,真夠扯的,不懂這樣爲了什麼  
  10.   
  11.     // sort the splits into order based on size, so that the biggest  
  12.     // go first  
  13.     Arrays.sort(array, new SplitComparator());//splits排序  
  14.     JobSplitWriter.createSplitFiles(jobSubmitDir, conf,  
  15.         jobSubmitDir.getFileSystem(conf), array);  
  16.     return array.length;//mapper數量  
  17.   }  

 

3.貼上最常用的FileInputSplit的getSplits方法

 

Java代碼  收藏代碼
  1. public List<InputSplit> getSplits(JobContext job  
  2.                                     ) throws IOException {  
  3.     long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));  
  4.     long maxSize = getMaxSplitSize(job);  
  5.   
  6.     // generate splits  
  7.     List<InputSplit> splits = new ArrayList<InputSplit>();  
  8.     List<FileStatus>files = listStatus(job);  
  9.     for (FileStatus file: files) {  
  10.       Path path = file.getPath();  
  11.       FileSystem fs = path.getFileSystem(job.getConfiguration());  
  12.       long length = file.getLen();  
  13.       BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length);  
  14.       if ((length != 0) && isSplitable(job, path)) {   
  15.         long blockSize = file.getBlockSize();  
  16.         long splitSize = computeSplitSize(blockSize, minSize, maxSize);//獲得split文件的最大文件大小  
  17.   
  18.         long bytesRemaining = length;  
  19.         while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {//分解大文件  
  20.           int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);  
  21.           splits.add(new FileSplit(path, length-bytesRemaining, splitSize,   
  22.                                    blkLocations[blkIndex].getHosts()));  
  23.           bytesRemaining -= splitSize;  
  24.         }  
  25.           
  26.         if (bytesRemaining != 0) {  
  27.           splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining,   
  28.                      blkLocations[blkLocations.length-1].getHosts()));  
  29.         }  
  30.       } else if (length != 0) {  
  31.         splits.add(new FileSplit(path, 0, length, blkLocations[0].getHosts()));  
  32.       } else {   
  33.         //Create empty hosts array for zero length files  
  34.         splits.add(new FileSplit(path, 0, length, new String[0]));  
  35.       }  
  36.     }  
  37.       
  38.     // Save the number of input files in the job-conf  
  39.     job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());  
  40.   
  41.     LOG.debug("Total # of splits: " + splits.size());  
  42.     return splits;  
  43.   }  
 

 

 

二、讀取keyvalue的過程

1.實例化inputformat,初始化reader

在MapTask類的runNewMapper方法中,生成inputformat和recordreader,並進行初始化,運行mapper

MapTask$NewTrackingRecordReader 由 RecordReader組成,是它的一個代理類

Java代碼  收藏代碼
  1.  private <INKEY,INVALUE,OUTKEY,OUTVALUE>  
  2.   void runNewMapper {  
  3.  // 生成自定義inputformat  
  4.     org.apache.hadoop.mapreduce.InputFormat<INKEY,INVALUE> inputFormat =  
  5.       (org.apache.hadoop.mapreduce.InputFormat<INKEY,INVALUE>)  
  6.         ReflectionUtils.newInstance(taskContext.getInputFormatClass(), job);  
  7. .....  
  8. //生成自定義recordreader  
  9. org.apache.hadoop.mapreduce.RecordReader<INKEY,INVALUE> input =  
  10.       new NewTrackingRecordReader<INKEY,INVALUE>  
  11.           (split, inputFormat, reporter, job, taskContext);  
  12. .....  
  13. //初始化recordreader  
  14. input.initialize(split, mapperContext);  
  15. .....  
  16. //運行mapper  
  17. mapper.run(mapperContext);  
  18.    }  

 

2.在運行mapper中,調用context讓reader讀取key和value,其中使用代理類MapTask$NewTrackingRecordReader,添加並推送讀取記錄

    mapper代碼:

Java代碼  收藏代碼
  1. public void run(Context context) throws IOException, InterruptedException {  
  2.    setup(context);  
  3.       
  4.    while (context.nextKeyValue()) {  
  5.      map(context.getCurrentKey(), context.getCurrentValue(), context);  
  6.    }  
  7.    cleanup(context);  
  8.  }  

 MapContext代碼:

Java代碼  收藏代碼
  1. @Override  
  2.   public boolean nextKeyValue() throws IOException, InterruptedException {  
  3.     return reader.nextKeyValue();  
  4.   }  
  5. @Override  
  6.   public KEYIN getCurrentKey() throws IOException, InterruptedException {  
  7.     return reader.getCurrentKey();  
  8.   }  
  9.   
  10.   @Override  
  11.   public VALUEIN getCurrentValue() throws IOException, InterruptedException {  
  12.     return reader.getCurrentValue();  
  13.   }  

 MapTask$NewTrackingRecordReader的代碼:

Java代碼  收藏代碼
  1. @Override  
  2.    public boolean nextKeyValue() throws IOException, InterruptedException {  
  3.      boolean result = false;  
  4.      try {  
  5.        long bytesInPrev = getInputBytes(fsStats);  
  6.        result = real.nextKeyValue();//recordreader實際讀取數據  
  7.        long bytesInCurr = getInputBytes(fsStats);  
  8.   
  9.        if (result) {  
  10.          inputRecordCounter.increment(1);//添加讀取記錄  
  11.          fileInputByteCounter.increment(bytesInCurr - bytesInPrev);//記錄讀取數據  
  12.        }  
  13.        reporter.setProgress(getProgress());//將reporter的flag置爲true,推送記錄信息  
  14.      } catch (IOException ioe) {  
  15.        if (inputSplit instanceof FileSplit) {  
  16.          FileSplit fileSplit = (FileSplit) inputSplit;  
  17.          LOG.error("IO error in map input file "  
  18.              + fileSplit.getPath().toString());  
  19.          throw new IOException("IO error in map input file "  
  20.              + fileSplit.getPath().toString(), ioe);  
  21.        }  
  22.        throw ioe;  
  23.      }  
  24.      return result;  
  25.    }  

 3.執行完mapper方法,返回到maptask,關閉reader

  

Java代碼  收藏代碼
  1. mapper.run(mapperContext);  
  2. input.close();//關閉inputformat  
  3. output.close(mapperContext);  

 

 兩個步驟不在同一個線程中完成,生成splits後進入monitor階段

以上也調用了所有的inputformat虛類的所有方法

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