• 输入的InputFormatSequenceFileInputFormat


    继承关系:SequenceFileInputFormat  extends FileInputFormat  implements InputFormat  。

    SequenceFileInputFormat 代码如下(其实很简单):

      /**
       * 覆盖了FileInputFormat的这个方法,FileInputFormat通过这个方法得到的FileStatus[]
       * 长度就是将要运行的map的长度,每个FileStatus对应一个文件
       */
      @Override
      protected FileStatus[] listStatus(JobConf job) throws IOException {
        FileStatus[] files = super.listStatus(job);
        /*调用父类的listStatus方法,然后进行了自己的处理,将得到的FileStatus[]遍历一遍,
                遇到文件夹时候,看其是否为MapFile,是的话去除其中也是SequenceFile的data文件,否则将文件夹过滤掉。
         */
        for (int i = 0; i < files.length; i++) {
          FileStatus file = files[i];
          if (file.isDir()) {     // it's a MapFile
            Path dataFile = new Path(file.getPath(), MapFile.DATA_FILE_NAME);
            FileSystem fs = file.getPath().getFileSystem(job);
            // use the data file
            files[i] = fs.getFileStatus(dataFile);
          }
        }
        return files;
      }

    下面看看FileInputFormat的listStatus(JobConf job)方法:

     protected FileStatus[] listStatus(JobConf job) throws IOException {
          //得到job配置中的所有输入路径,路径中是以 ,号隔开的 。
          Path[] dirs = getInputPaths(job);
          if (dirs.length == 0) {
              throw new IOException("No input paths specified in job");
          }
    
        // get tokens for all the required FileSystems..
        TokenCache.obtainTokensForNamenodes(job.getCredentials(), dirs, job);
        
        List<FileStatus> result = new ArrayList<FileStatus>();
        List<IOException> errors = new ArrayList<IOException>();
        
        // creates a MultiPathFilter with the hiddenFileFilter and the
        // user provided one (if any).
        //处理一个过路文件的filter,可以将输入文件夹中的一些文件过滤掉
        List<PathFilter> filters = new ArrayList<PathFilter>();
        filters.add(hiddenFileFilter);
        PathFilter jobFilter = getInputPathFilter(job);
        if (jobFilter != null) {
          filters.add(jobFilter);
        }
        PathFilter inputFilter = new MultiPathFilter(filters);
        //对于每个输入文件夹进行遍历
        for (Path p: dirs) {
          FileSystem fs = p.getFileSystem(job); 
          //得到这个输入文件下下的所有文件(夹)
          FileStatus[] matches = fs.globStatus(p, inputFilter);
          if (matches == null) {
            errors.add(new IOException("Input path does not exist: " + p));
          } else if (matches.length == 0) {
            errors.add(new IOException("Input Pattern " + p + " matches 0 files"));
          } else {
            //遍历输入问价夹下的每个文件(夹)
            for (FileStatus globStat: matches) {
              if (globStat.isDir()) {
                //文件夹的话,将该文件夹下的所有文件和文件夹添加到结果中
                //****注意此处没有再往下层遍历,而是将文件和文件夹都返回到结果中 。
                for(FileStatus stat: fs.listStatus(globStat.getPath(),
                    inputFilter)) {
                  result.add(stat);
                }          
              } else {
                //文件的话直接添加到result中,其实没有任何判断该文件是否是输入需要的格式等等
                result.add(globStat);
              }
            }
          }
        }
    
        if (!errors.isEmpty()) {
          throw new InvalidInputException(errors);
        }
        LOG.info("Total input paths to process : " + result.size()); 
        return result.toArray(new FileStatus[result.size()]);
      }

    是以总结SequenceFileInputFormat中输出文件的规律(假设输入文件夹是/input ):

    1、输入文件夹中的文件 ,即满足:/input/***文件 。

    2、输入文件夹中子文件夹中的文件 ,/input/***/***文件 。

    3、输入文件夹中的子文件夹的子文件夹中的data文件 ,/input/***/***/data文件 ,该主要是针对MapFile的  。

    得到一个个文件后,怎么将文件映射到InputSplit(有可能一个file映射1个InputSplit,也可能映射几个InputSplit),代码见下:

    /** Splits files returned by {@link #listStatus(JobConf)} when
       * they're too big.*/ 
      @SuppressWarnings("deprecation")
      public InputSplit[] getSplits(JobConf job, int numSplits)
        throws IOException {
        FileStatus[] files = listStatus(job);
        
        // Save the number of input files in the job-conf
        job.setLong(NUM_INPUT_FILES, files.length);
        long totalSize = 0;                           // compute total size
        for (FileStatus file: files) {                // check we have valid files
          if (file.isDir()) {
            throw new IOException("Not a file: "+ file.getPath());
          }
          totalSize += file.getLen();
        }
    
        long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);
        long minSize = Math.max(job.getLong("mapred.min.split.size", 1),
                                minSplitSize);
    
        // generate splits
        ArrayList<FileSplit> splits = new ArrayList<FileSplit>(numSplits);
        NetworkTopology clusterMap = new NetworkTopology();
        //对于每个文件该分割成多少InputSplit的处理
        for (FileStatus file: files) {
          Path path = file.getPath();
          FileSystem fs = path.getFileSystem(job);
          long length = file.getLen();
          BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length);
          //允许切割文件时候,即允许将一个file切割成多个InputSplit 。
          if ((length != 0) && isSplitable(fs, path)) { 
            long blockSize = file.getBlockSize();
            //分块的大小,一般是以快为单位,即会选择blockSize ,其实会将按照block来分块,这样比较合适
            long splitSize = computeSplitSize(goalSize, minSize, blockSize);
    
            long bytesRemaining = length;
            //按照分割设置(每块大小),将文件从从offset=0 到offset=length 分割成 (length/splitSize+1) 个(其实也并不是这些个啦 ~ ~ ,最后一块的大小可以是splitSize*SPLIT_SLOP) 
            while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
              String[] splitHosts = getSplitHosts(blkLocations, 
                  length-bytesRemaining, splitSize, clusterMap);
              splits.add(new FileSplit(path, length-bytesRemaining, splitSize, 
                  splitHosts));
              bytesRemaining -= splitSize;
            }
            //返回splits
            if (bytesRemaining != 0) {
              splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining, 
                         blkLocations[blkLocations.length-1].getHosts()));
            }
          } else if (length != 0) {
            String[] splitHosts = getSplitHosts(blkLocations,0,length,clusterMap);
            splits.add(new FileSplit(path, 0, length, splitHosts));
          } else { 
            //Create empty hosts array for zero length files
            splits.add(new FileSplit(path, 0, length, new String[0]));
          }
        }
        LOG.debug("Total # of splits: " + splits.size());
        return splits.toArray(new FileSplit[splits.size()]);
      }

    ok ,一切都完了 ,但是你看上面的代码可能会产生一个疑问:SequenceFile是存储的一个个的key-value值,这样分割文件的话,会不会破坏原有的数据结构,即 要是某一个key-value被分到了两个FileSplit肿么办 ?

    见文章:http://www.cnblogs.com/serendipity/articles/2112613.html

    个人感觉mapreduce的这个工具InputFormat比较乱,往往不看源代码,你永远也无法知道到底哪些文件被选上了  。

  • 相关阅读:
    Java Bean validation specification...
    javascript压缩工具:YUI compressor
    CSS: Float a div on another div, Ex: Text caption on picture
    汉文博士 0.5.3.1944 测试版发布
    简单记事本程序java源码项目
    Windows计算机功能Java源码
    HighCharts 图表插件 自定义绑定 时间轴数据
    Windows Phone 8.1 新特性
    可自定义导航条功能案例ios项目源码
    自动计算尺寸列表功能案例ios源码
  • 原文地址:https://www.cnblogs.com/serendipity/p/2473473.html
Copyright © 2020-2023  润新知