(三)hadoop中FileInputFormat类的getSplits获取InputSplit的过程
来源:互联网 发布:蛇蛇大作战网络未连接 编辑:程序博客网 时间:2024/06/06 00:50
FileInputFormat继承了抽象类InputFormat,来看一下InputFormat的源码:
public abstract class InputFormat<K, V> { public abstract List<InputSplit> getSplits(JobContext context) throws IOException, InterruptedException; public abstract RecordReader<K,V> createRecordReader(InputSplit split,TaskAttemptContext context) throws IOException, InterruptedException;}
InputFormat 主要用于描述输入数据的格式, 它提供以下两个功能。
(1)数据切分 : 按照某个策略将输入数据切分成若干个 InputSplit, 以便确定 Map Task 个数。对应的就是getSplits方法。
(2)为 Mapper 提供输入数据: 给定某个 InputSplit, 能将其解析成一个个 key/value 对。对应的就是createRecordReader方法。
getSplits 方法主要完成数据切分的功能, 它会尝试着将输入数据切分成 InputSplit,并放入集合List中返回。
InputSplit有以下特点:
(1)逻辑分片 : 它只是在逻辑上对输入数据进行分片, 并不会在磁盘上将其切分成分片进行存储。 InputSplit 只记录了分片的元数据信息, 比如起始位置、 长度以及所在的节点列表等。
(2)可序列化: 在 Hadoop 中, 对象序列化主要有两个作用: 进程间通信和永久存储。 此处, InputSplit 支持序列化操作主要是为了进程间通信。 作业被提交到 JobTracker 之前, Client 会调用作业 InputFormat 中的 getSplits 函数, 并将得到的 InputSplit 序列
化到文件中。 这样, 当作业提交到 JobTracker 端对作业初始化时, 可直接读取该文件, 解析出所有 InputSplit, 并创建对应的 Map Task。而createRecordReader则根据InputSplit ,将其解析成一个个 key/value 对。
现在再来看FileInputFormat中对这两个方法的具体实现。
先来看FileInputFormat的定义:
public abstract class FileInputFormat<K, V> extends InputFormat<K, V> {}
FileInputFormat也是一个抽象类,继承了InputFormat这个抽象类。
再来看看FileInputFormat关于getSplits的实现,源码如下:
public List<InputSplit> getSplits(JobContext job) throws IOException { 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(); FileSystem fs = path.getFileSystem(job.getConfiguration()); long length = file.getLen(); BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length); if ((length != 0) && 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(new FileSplit(path, length-bytesRemaining, splitSize, blkLocations[blkIndex].getHosts())); bytesRemaining -= splitSize; } if (bytesRemaining != 0) { splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining, blkLocations[blkLocations.length-1].getHosts())); } } else if (length != 0) { splits.add(new FileSplit(path, 0, length, blkLocations[0].getHosts())); } else { //Create empty hosts array for zero length files splits.add(new FileSplit(path, 0, length, new String[0])); }}
我们来一块一块的分析,
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));long maxSize = getMaxSplitSize(job);
不难理解,这里是获取InputSplit的size的最小值和最大值,最小值minSize是通过取getFormatMinSplitSize()和getMinSplitSize(job))中的较大的值
getFormatMinSplitSize方法的源码如下:
protected long getFormatMinSplitSize() { return 1;}
直接返回1,单位是B.
而getMinSplitSize方法的源码如下:
public static long getMinSplitSize(JobContext job) { return job.getConfiguration().getLong("mapred.min.split.size", 1L);}
返回的是从配置文件中读取“mapred.min.split.size”属性的value值,“mapred.min.split.size”是需要用户自己添加配置的,配置在mapred-site.xml文件中。
这样一来,minSize的值取决于用户配置的mapred.min.split.size和1B中的较大值。
maxSize的大小是由getMaxSplitSize方法确定的,源码如下:
public static long getMaxSplitSize(JobContext context) { return context.getConfiguration().getLong("mapred.max.split.size",Long.MAX_VALUE);}
若“mapred.max.split.size”属性值读取不到,则返回Long.MAX_VALUE,否则返回“mapred.max.split.size”属性的值。
getSplits方法中有一句代码值得关注:
long splitSize = computeSplitSize(blockSize, minSize, maxSize);
这里是确定了InputSplit的大小,computeSplitSize方法源码如下:
protected long computeSplitSize(long blockSize, long minSize,long maxSize) { return Math.max(minSize, Math.min(maxSize, blockSize));}
上面说过,minSize的值取决于用户配置的mapred.min.split.size和1B中的较大值。 maxSize的值取决于用户配置的mapred.max.split.size和Long.MAX_VALUE中的较大值。blockSize则是HDFS的默认块大小。
获取到splitSize 后,文件将被切分成大小为splitSize的InputSplit,最后剩下不足splitSize的数据块单独成为一个InputSplit。
那接下来毫无疑问就是按照splitSize 来切分文件了(逻辑上的切分)。
再看getSplits方法的代码块:
long bytesRemaining = length;while (((double) bytesRemaining)/splitSize >SPLIT_SLOP) { int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining); splits.add(new FileSplit(path, length-bytesRemaining, splitSize, blkLocations[blkIndex].getHosts())); bytesRemaining -= splitSize;}if (bytesRemaining != 0) { splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining, blkLocations[blkLocations.length-1].getHosts()));}
这里就是切分的核心代码了,bytesRemaining 表示的是切分后,剩余的待切分的文件大小,初始值就是文件大小【length】,splitSize就是InputSplit的大小,SPLIT_SLOP是一个常量值,定义如下:
private static final double SPLIT_SLOP = 1.1;//10% slop
意思就是当剩余文件大小bytesRemaining与splitSize的比值还大于1.1的时候,就继续切分,否则,剩下的直接作为一个InputSplit。
敲黑板,划重点:并不一定非得bytesRemaining小于splitSize才停止划分哦,只要bytesRemaining/splitSize<=1.1就会停止划分,将剩下的作为一个InputSplit
我们还可以看到,
splits.add(new FileSplit(path, length-bytesRemaining, splitSize,blkLocations[blkIndex].getHosts()));
这里四个参数表示的意思是该InputSplit所在的(路径,起始位置,大小,所在的 host(节点) 列表)
以上就知道getSplits获取InputSplit的过程。
- (三)hadoop中FileInputFormat类的getSplits获取InputSplit的过程
- Hadoop中 MapReduce中InputSplit的分析
- Hadoop新版和旧版中InputSplit大小的区别
- Hadoop Map/Reduce 新API中自己的FileInputFormat写法
- Hadoop Map/Reduce 新API中自己的FileInputFormat写法
- Hadoop 2.6.0 FileSplit和InputSplit和FileInputFormat
- 在Hadoop中重写FileInputFormat类以处理二进制格式存储的整数
- FileInputFormat类的输入路径
- 在mapper中获得inputsplit的信息
- 在mapper中获得inputsplit的信息
- MapReduce中跨InputSplit数据的处理
- Hadoop中FileInputFormat源码解析
- MapReduce(MR)的文件拆分:FileInputFormat
- MapReduce中如何处理跨行的Block和InputSplit
- mapreduce的文件拆分,FileInputFormat
- mapreduce的文件拆分,FileInputFormat
- Hadoop MapReduce中如何处理跨行Block和inputSplit
- Hadoop MapReduce中如何处理跨行Block和InputSplit
- Java上机心得1
- MySQL数据库之单表的DQL
- Python oj 的网站
- Tomcat下载搭建环境
- CentOS7 通过yum安装dnf失败,提示No package dnf available的解决办法
- (三)hadoop中FileInputFormat类的getSplits获取InputSplit的过程
- Spark
- 有关AsyncTask的一些随笔笔记
- opencv学习历程003(使用Cmake编译OpenCV源代码的相关问题)
- laravel分页参数设置
- npm install ionic cordova -g
- P、NP、NP-hard、NP-complete问题
- 对比快速排序,理解归并排序
- Java中的构造方法总结和this及super的使用