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);执行了
*/
}