本片文章开始整理FileInputFormat切片源码解析(input.getSplits(job))的学习笔记
获取切片的代码流程
- 设置minSize的初始值,做法是取最小切片的大小和配置切片的最小值中的最大值作为minSize
- 设置maxSize -> 取最大切片值
- 创建一个切片的集合
- 获取要进行切片的输入目录
- 遍历输入目录
- 获取文件路径
- 获取文件长度
- 如果文件的长度不为零,就进行切片操作,如果为零,就向切片的集合添加一个零长度的文件
- 先判断要切片的文件是否在本地还是网络,如果是网络的就直接获取块的位置信息,如果是本地的,就要获取块的本地信息,外加文件的长度
- 然后进行判断是否是可以进行切片
- 如果可以切片
- 先获取文件的块大小
- 计算切片的大小
Math.max(minSize, Math.min(maxSize, blockSize));
- 然后对文件进行切片,将切的片放入切片集合中
- 如果不可以切片,就直接将文件的所有放入切片集合中
- 返回切片集合
getSplits源码解析
public List<InputSplit> getSplits(JobContext job) throws IOException {
// 开启一个计时器,计时的单位是纳秒
StopWatch sw = new StopWatch().start();
// 设置minSize的初始值,做法是取最小切片的大小和配置切片的最小值中的最大值作为minSize
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
// 设置maxSize -> 取最大切片值
long maxSize = getMaxSplitSize(job);
// 创建切片
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 {
// 如果设置为不可切片的话就直接将文件的全部放入切片
splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts(),
blkLocations[0].getCachedHosts()));
}
} else {
// 创建一个空的主机列表(包含0长度的文件)
splits.add(makeSplit(path, 0, length, new String[0]));
}
}
// 保存输入文件的数量和长度以用于度量标准/ 负载均衡
job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
// 停止计时
sw.stop();
// 返回切片信息
return splits;
}
computeSplitSize源码解析
重点!这个代码是计算切片大小的,算法就是取最小长度和(最大长度和块的长度)的最小值的最大值,但是一般取得就是配置文件中的块的长度,在hadoop 1.X时代是64M,在hadoop 2.X时代是128M,但是运行在local中的时候默认为32M
protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}