RecordReader:其作用就是将数据切分成key/value的形式然后作为输入传给Mapper。
一 方法分析:
1.1initialize: 初始化RecordReader,只能被调用一次。
1.2nextKeyValue: 读取下一个key/value键值对
1.3getCurrentKey: 获取当前的key
1.4getCurrentValue: 获取当前的value
1.5getProgress: 进度
1.6close: 关闭RecordReader
二 RecordReader运行流程
2.1 MapTask会构造一个NewTrackingRecordReader对象
2.2在执行Mapper#run方法之前会调用RecordReader的initialize方法初始化一些东西。
2.3在这个初始化方法里面,它会调用InputFormat#createRecordRead
er方法,我们知道默认的InputFormat就是TextInputFormat,所以这里就回到用TextInputFormat#createRecordReader,返回LineRecordRead
er。
2.4 初始化方法他会干这几样事情:
#将InputSplit转成FileSplit
#获取每一行能读取的最大的长度默认是Integer.MAX_VALUE
#获取当前FileSplit的开始位置
#获取当前FileSplit的结束位置
#获取当前FileSplit的文件路径
2.5 然后进入Mapper#run方法,首先会判断是否有下一个key/value,如果有则传入当前的key和value到map方法.
2.6 LineRecordReader是将当前行开始位置的offset作为key,当前行的值作为value传入给map方法
2.7 程序运行完毕,关闭RecordReader
三 常见的RecordReader
LineRecordReader:将文本行开始的便宜量作为key,整个文本行作为value
CombineFileRecordReader:处理CombineInputSplit里的每一个chunk
的RecordReader,CombineInputSplit包含不同的小文件chunk信息
但是具体读取每一个文件的数据,是由单独的RecordReader来读取的,CombineFileRecordReader只负责操作chunk数据
DBRecordReader:从数据库表中读取数据
KeyValueRecordReader:根据指定的分割符却切分每一行数据,如果没有指定分割符,那么key就是整行的文本,value就是空
四 自定义RecordReader
public static class CombineFileLineRecordReader
extends RecordReader
private long startOffset; //offset of the chunk;
private long end; //end of the chunk;
private long pos; // current pos
private FileSystem fs;
private Pathpath;
private WordOffsetkey;
private Textvalue;
private FSDataInputStreamfileIn;
private LineReaderreader;
public CombineFileLineRecordReader(CombineFileSplit split,
TaskAttemptContext context, Integer index) throws IOException {
this.path =split.getPath(index);
fs = this.path.getFileSystem(context.getConfiguration());
this.startOffset =split.getOffset(index);
this.end =startOffset + split.getLength(index);
boolean skipFirstLine = false;
//open the file
fileIn = fs.open(path);
if (startOffset !=0) {
skipFirstLine = true;
--startOffset;
fileIn.seek(startOffset);
}
reader = newLineReader(fileIn);
if (skipFirstLine) { // skip first line and re-establish "startOffset".
startOffset += reader.readLine(newText(), 0,
(int)Math.min((long)Integer.MAX_VALUE,end - startOffset));
}
this.pos =startOffset;
}
public void initialize(InputSplitsplit, TaskAttemptContextcontext)
throws IOException, InterruptedException {
}
public void close() throws IOException { }
public float getProgress() throws IOException {
if (startOffset ==end) {
return0.0f;
} else {
return Math.min(1.0f, (pos -startOffset) / (float)(end -startOffset));
}
}
public boolean nextKeyValue() throws IOException {
if (key ==null) {
key = newWordOffset();
key.fileName =path.getName();
}
key.offset =pos;
if (value ==null) {
value = newText();
}
int newSize = 0;
if (pos <end) {
newSize = reader.readLine(value);
pos += newSize;
}
if (newSize ==0) {
key = null;
value = null;
return false;
} else {
return true;
}
}
public WordOffsetgetCurrentKey()
throws IOException, InterruptedException {
return key;
}
public TextgetCurrentValue() throws IOException, InterruptedException {
return value;
}
}