Hadoop源码解读(切片原理)

Hadoop源码解读(切片原理)

在上一篇博客中。主要聊了聊一个hadoop的job在提交后,运行前所做的一些准备工作。主要是:

  1. 获取Job运行的环境是LocalJobRunner还是YarnRunner。
  2. 校验输出路径
  3. 获取Job的工作目录
  4. 获取当前将要运行的Job的Id
  5. 拼接上面两个,生成当前Job的工作目录,并创建
  6. 生成切片信息,返回切片的数量,并将切片信息放入Job工作目录中
  7. 将当前Job配置信息放入Job工作目录中

这篇博客中主要聊一聊第6步,也就是生成切片信息的过程。上一个博客讲到int maps = writeSplits(job, submitJobDir)这个方法时,就没有细讲,这次我们详细聊一聊切片的过程。入点从int maps = writeSplits(job, submitJobDir);方法开始。

int maps = writeSplits(job, submitJobDir);hadoop切片的主要逻辑都在这个方法中,所以我们进到这个方法里来研究下。代码如下:

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;
  }
  1. map表示切片的个数

  2. maps = writeNewSplits(job, jobSubmitDir);因为是用的新的API,切片个数是调用

    maps = writeNewSplits(job, jobSubmitDir);方法得到

    具体方法如下:

    private <T extends InputSplit>
      int writeNewSplits(JobContext job, Path jobSubmitDir) throws IOException,
          InterruptedException, ClassNotFoundException {
        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 first
        Arrays.sort(array, new SplitComparator());
        JobSplitWriter.createSplitFiles(jobSubmitDir, conf, 
            jobSubmitDir.getFileSystem(conf), array);
        return array.length;
      }
    
    • splits是一个List集合,里面存放的是每个切片信息,通过将splits变成一个array数组,返回这个array数组的length,来返回切片的个数
    • 在这个方法里面最核心的是List splits = input.getSplits(job);这个方法获取切片信息,并将每个切片的信息放入到splits这个List集合中,所以我们现在来专门研究下这个方法,以下是这个方法的实现:
      public List<InputSplit> getSplits(JobContext job) throws IOException {
        StopWatch sw = new StopWatch().start();
        long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
        long maxSize = getMaxSplitSize(job);
    
        // generate splits
        List<InputSplit> splits = new ArrayList<InputSplit>();
        List<FileStatus> files = listStatus(job);
        for (FileStatus file: files) {
          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();
              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(),
                            blkLocations[blkIndex].getCachedHosts()));
                bytesRemaining -= splitSize;
              }
    
              if (bytesRemaining != 0) {
                int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
                splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
                           blkLocations[blkIndex].getHosts(),
                           blkLocations[blkIndex].getCachedHosts()));
              }
            } else { // not splitable
              splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts(),
                          blkLocations[0].getCachedHosts()));
            }
          } else { 
            //Create empty hosts array for zero length files
            splits.add(makeSplit(path, 0, length, new String[0]));
          }
        }
        // Save the number of input files for metrics/loadgen
        job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
        sw.stop();
        if (LOG.isDebugEnabled()) {
          LOG.debug("Total # of splits generated by getSplits: " + splits.size()
              + ", TimeTaken: " + sw.now(TimeUnit.MILLISECONDS));
        }
        return splits;
      }
    

    ​ ①在这里我挑几个重点的来聊一聊

    ​ ②long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));

    ​ 这个minSize默认为1

    ​ ③long maxSize = getMaxSplitSize(job);这个是获取maxSize,具体我们可以看看他是如何实现的

     public static long getMaxSplitSize(JobContext context) {
        return context.getConfiguration().getLong(SPLIT_MAXSIZE, 
                                                  Long.MAX_VALUE);
      }
    

    可以看到传入的SPLIT_MAXSIZE和Long类型的最大值。SPLIT_MAXSIZE默认为null。

     public long getLong(String name, long defaultValue) {
        String valueString = getTrimmed(name);
        if (valueString == null)
          return defaultValue;
        String hexString = getHexDigits(valueString);
        if (hexString != null) {
          return Long.parseLong(hexString, 16);
        }
        return Long.parseLong(valueString);
      }
    

    可以看到调用这个方法的两个参数,一个为null,另一个为Long类型的最大值。最终的结果为Long类型的最大值

    ​ ④List splits = new ArrayList();表示存放切片信息的List集合

    ​ ⑤List files = listStatus(job);表示获得job提交时所有输入的文件

    ​ ⑥对每一个文件进行遍历,这样就默认一个文件就是一个切片。现在后续的代码就是看你每一个文件是否 可以再进行切片。

    ​ ⑦Path path = file.getPath();获取文件的具体路径和文件名

    ​ long length = file.getLen();获取当前文件的大小

    ​ ⑧

      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();
                 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(),
                               blkLocations[blkIndex].getCachedHosts()));
                   bytesRemaining -= splitSize;
                 }
    

    查看当前文件是否可以切分。因为有的文件无法切分。如果无法切分的文件,就只能当做一个切片处理。这里面有一个方法比较重要。就是计算切片大小的方法。因为你一个文件想要切片,你就需要知道,是按照什么切片大小进行切分。所以切片大小很重要。这里我们详细解读下计算切片大小这个方法。以下为这个方法的具体实现:

    protected long computeSplitSize(long blockSize, long minSize,
                                      long maxSize) {
        return Math.max(minSize, Math.min(maxSize, blockSize));
      }
    
    • blockSize是调用这个方法时,传过来的参数,long blockSize = file.getBlockSize();我这边具体大小为32M。在这里我大概说下这个blockSize的值。如果是在本地的话,大小为32M,如果是在hadoop1.x版本,大小为64M。hadoop2.x版本,大小为128M。

    • minSize就是之前在第2点我们得到的1

    • maxSize也是之前在第3点得到的Long的最大值

    • 所以最终计算出来的切片大小为32M。是块的大小

    ⑩计算完切片大小后,我们来对文件进行切分。具体步骤就是

    ((double) bytesRemaining)/splitSize > SPLIT_SLOP
    

    如果当前文件大小与切片大小进行相除,结果大于SPLIT_SLOP,也就是1.1,就将文件切一片,并把这个切片的具体信息加入到之前的splits集合中。然后将文件大小减去这个切片大小后,再进行判断是否还是((double) bytesRemaining)/splitSize > SPLIT_SLOP成立,一直循环,知道这个条件不成立。并最终将剩余的切片信息也加入到splits集合中。

    3.JobSplitWriter.createSplitFiles(jobSubmitDir, conf, jobSubmitDir.getFileSystem(conf), array);

    将切片信息写入到Job工作目录下

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值