一 InputFormat主要作用:
#验证job的输入规范
#对输入的文件进行切分,形成多个InputSplit文件,每一个InputSplit对应着一个map任务
#创建RecordReader,从InputSplit分片中读取数据供map使用
二 有几个比较重要的实现
2.1FileInputFormat: 主要用于处理文件的一个InputFormat类,它包括子类:
2.1.1 FiexedLengthInputFormat: 读取输入文件的固定长度的记录,这种文件不该是文本文件,二进制文件比较多
2.1.2KeyValueInputFormat: 用于读取普通文本文件,文件按照行分割,每一行由key和value组成,key 和 value的分隔符若没有指定,那么整行为key,value为空
2.1.3TextInputFormat: 文件按照行划分,key就是这一行在文件中的偏移量,value就是这一行文本
2.1.5SequenceFileInputFormat:
=>SequenceFileAsBinaryInputFormat:InputFormat从sequenceFile读取以二进制格式读取key和value. 他只能读取SequenceFile
=>SequenceFileAsTextInputFormat:以文本格式读取
2.1.6NLineInputFormat: 是可以将N行数据划分为一个Split,作为MapTask输入
2.2DBInputFormat: 主要用于处理数据库数据的InputFormat类
2.3CombineFileInputFormat:
我们知道文件切分的时候,FileInputFormat默认情况下,如果一个文件小于blockSize,那么这个文件就是一个InputSplits, 如果大于blockSize,则是先按照文件大小/blockSize,如果有剩余单独成为一个InputSplit。
但是如果小文件太多,那么就会生成的切片太多,从而导致Map任务增多,Map任务增多所需资源就多。所以CombineFileInputFormat通过合并多个小文件成为一个分片,分片过程也考虑同一节点,同一机架的数据本地性,让每一个Mapper任务可以处理更多的数据
三 CombineFileInputFormat详解
我们知道,InputFormat接口,需要我们自己提供RecordReader.
它的原理就是:
我们通常大文件都是使用FileSplit,但是现在是包含多个文件的split,我们需要使用CombineFileSplit。它会将输入多个数据文件(小文件)元数据全部包装到CombineFileSplit里面。因为小文件都是单独的一个Block文件,一个CombineFileSplit包含一组文件的Block信息,涉及到每个文件的偏移,长度,block位置等元数据.
在执行Map-Reduce的时候,需要读取文本数据,那么对于Combine
FileSplit,你需要处理其包含的小文件block,就要对应设置RecordReader,才能正确读取文件数据内容。
编程流程:
#实现一个RecordReader来读取CombineFileSplit包装的Block
#继承CombineFileInputFormat实现一个我们自定义的RecordReader的类,用来读取每个文件的数据
public class MyCombineFileRecordReader extends RecordReader
private CombineFileSplit combineFileSplit;//封装了小文件的meta信息,诸如长度,offset以及块的信息
private LineRecordReader lineRecordReader = new LineRecordReader();//用于读取行数据
private Path[] paths; //小文件的输入路径
private int length;
private int currentIndex;
private float currentProgress = 0;
private LongWritable currentKey;
private BytesWritable currentValue = new BytesWritable();
public MyCombineFileRecordReader(CombineFileSplit combineFileSplit, TaskAttemptContext context, Integer index){
super();
this.combineFileSplit = combineFileSplit;
//当前要处理的小文件block在CombineFileSplit中的索引
this.currentIndex = index;
}
@Override
public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
this.combineFileSplit = (CombineFileSplit) split;
this.paths = combineFileSplit.getPaths();
this.length = paths.length;
// 处理CombineFileSplit中的一个小文件Block,因为使用LineRecordReader,
//需要构造一个FileSplit对象,然后才能够读取数据
//当前小文件的路径
Path path = combineFileSplit.getPath(currentIndex);
//当前小文件的偏移量
long start = combineFileSplit.getOffset(currentIndex);
//当前小文件的长度
long length = combineFileSplit.getLength(currentIndex);
//小文件block所在的节点信息
String[] hosts = combineFileSplit.getLocations();
FileSplit fileSplit = new FileSplit(path, start, length, hosts);
lineRecordReader.initialize(fileSplit, context);
context.getConfiguration().set("map.input.file.name", path.getName());
}
@Override
public boolean nextKeyValue() throws IOException, InterruptedException {
return currentIndex >= 0&& currentIndex < length && lineRecordReader.nextKeyValue();
}
@Override
public LongWritable getCurrentKey() throws IOException, InterruptedException {
return lineRecordReader.getCurrentKey();
}
@Override
public BytesWritable getCurrentValue() throws IOException, InterruptedException {
byte[] contents = lineRecordReader.getCurrentValue().getBytes();
int len = contents.length;
currentValue.set(contents, 0, len);
return currentValue;
}
@Override
public float getProgress() throws IOException, InterruptedException {
if (currentIndex >= 0&& currentIndex < length) {
currentProgress = (float) currentIndex / length;
return currentProgress;
}
return currentProgress;
}
@Override
public void close() throws IOException {
lineRecordReader.close();
}
}
public class MyCombineFileInputFormat extends CombineFileInputFormat
@Override
public RecordReader
CombineFileSplit combineFileSplit = (CombineFileSplit) split;
CombineFileRecordReader
new CombineFileRecordReader
try {
recordReader.initialize(combineFileSplit, context);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return recordReader;
}
}