切片原理

1.InputFormat

当数据进入到Map时,需要用到Inputformat抽象类,抽象类中有两个抽象方法

public abstract class InputFormat<K, V> {
     
    
    // 获取切片信息
  public abstract 
    List<InputSplit> getSplits(JobContext context
                               ) throws IOException, InterruptedException;
	// 创建RecordReader对象,该对象是真正用来读取数据的对象。
  public abstract 
    RecordReader<K,V> createRecordReader(InputSplit split,
                                         TaskAttemptContext context
                                        ) throws IOException, 
                                                 InterruptedException;

}


2.FileInputFormat

/**
* 作为子类FileInputFormat也是一个抽象类,该类实现了父类InputFormat中getSplits方法
*
*/

public List<InputSplit> getSplits(JobContext job) throws IOException {
     
    StopWatch sw = new StopWatch().start();
    
    /*
    通过job对象拿到配置文件获得值: 
    SPLIT_MINSIZE("mapreduce.input.fileinputformat.split.minsize")
    如果没有配置就返回1,配置了就返回配置的值(在Driver中配置的值)	 	
   	public static long getMinSplitSize(JobContext job) {
    	return job.getConfiguration().getLong(SPLIT_MINSIZE, 1L);
    }
    
    getFormatMinSplitSize():值返回为1
    
    获取切片的最小值,若不配置返回minSize就为1,若有配置返回minSize就为调整的大小
    */
    long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
    
    /*
    通过配置文件获取SPLIT_MAXSIZE的值("mapreduce.input.fileinputformat.split.maxsize")
   	如果没有配置就返回默认值Long.MAX_VALUE:0x7fffffffffffffff
	public static long getMaxSplitSize(JobContext context) {
    	return context.getConfiguration().getLong(SPLIT_MAXSIZE, 
                                              Long.MAX_VALUE);
    
    获取切片最大值,若是不配置就返回默认值。
    */
    long maxSize = getMaxSplitSize(job);

    // generate splits
    List<InputSplit> splits = new ArrayList<InputSplit>();
    List<FileStatus> files = listStatus(job);

    boolean ignoreDirs = !getInputDirRecursive(job)
      && job.getConfiguration().getBoolean(INPUT_DIR_NONRECURSIVE_IGNORE_SUBDIRS, false);
    // 遍历files中的内容
    for (FileStatus file: files) {
     
        //如果是目录或者忽略的就不切片了
      if (ignoreDirs && file.isDirectory()) {
     
        continue;
      }
        //获取路径和文件长度
      Path path = file.getPath();
      long length = file.getLen();
        
        //校验
      if (length != 0) {
     
        BlockLocation[] blkLocations;
        if (file instanceof LocatedFileStatus) {
     
          blkLocations = ((LocatedFileStatus) file).getBlockLocations();
        } else {
     
          FileSystem fs = path.getFileSystem(job.getConfiguration());
          blkLocations = fs.getFileBlockLocations(file, 0, length);
        }
          //判断文件是否可切
        if (isSplitable(job, path)) {
     
            //获取块大小
          long blockSize = file.getBlockSize();
            
            /*
            获取切片大小:
            blockSize:128M
            minSize:1
            maxSize:0x7fffffffffffffff
			protected long computeSplitSize(long blockSize, long minSize,
                                  long maxSize) {
                
                //返回切片大小128M(如果想要调整切片大小:调整maxSize(可使切片大小>块大小))
                //调整minSize(可是切片大小<块大小)但是要考虑跨界点读取,具体情况具体分析了
    			return Math.max(minSize, Math.min(maxSize, blockSize));
  			}
            */
          long splitSize = computeSplitSize(blockSize, minSize, maxSize);
			
            //文件剩余大小
          long bytesRemaining = length;
            
            /*	
            	判断是否可以切片:文件剩余大小/片大小 > 1.1就为true,表示可以切片
            	来回循环直到文件剩余大小/片大小 < 1.1
            	这里SPLIT_SLOP不选择1.0,考虑到为剩余大小开启一个MapTask会消耗资源
            */
          while (((double) bytesRemaining)/splitSize小于1.0大于0的时候在这里切片  > SPLIT_SLOP) {
     
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
              
              /*
                如果可以切片就调用makeSplit()获取
            	protected FileSplit makeSplit(Path file, long start, long length, 
                                String[] hosts, String[] inMemoryHosts)
                Path file:获取路径
                length-bytesRemaining:切片开始的位置
                splitSize:偏移量(长度)
                blkLocations[blkIndex].getHosts():更据数组的索引获取地址
                blkLocations[blkIndex].getCachedHosts():获取副本的值
                
                将生成信息的对象放入splits数组中
              */
            splits.add(makeSplit(path, length-bytesRemaining, splitSize,
                        blkLocations[blkIndex].getHosts(),
                        blkLocations[blkIndex].getCachedHosts()));
              //剩余的大小
            bytesRemaining -= splitSize;
          }

            /*
            	当剩余bytesRemaining/splitSize小于1.0大于0的时候在这里切片 
            */
          if (bytesRemaining != 0) {
     
              //获取块的索引位置
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
              
              /*
              	当剩余的 bytesRemaining/片大小于1.1且不为零时,整体且成一片
              	将生成信息的对象放入splits数组中
              */
            splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
                       blkLocations[blkIndex].getHosts(),
                       blkLocations[blkIndex].getCachedHosts()));
          }
        } 
          ...
              /*
              剩下的就让他跑吧.... 最后在JobSubmitter中的int maps = writeSplits(job, 					submitJobDir);执行了
              */
  }

你可能感兴趣的:(Hadoop,java,hadoop)