我一直在尝试使用Hadoop将N行数发送到单个映射。我不需要拆分线路。
我尝试使用NLineInputFormat,但是每次将一行N行文本从数据发送到每个映射器[在第N行之后放弃]。
我试图设置该选项,它只需要N行输入一次一行地发送给每个地图:
job.setInt("mapred.line.input.format.linespermap", 10);
我找到了一个邮件列表,建议我覆盖LineRecordReader :: next,但这并不是那么简单,因为内部数据成员都是私有的。
我刚检查了NLineInputFormat的源代码,并且硬编码LineReader,所以覆盖无效。
另外,顺便说一句,我正在使用Hadoop 0.18与Amazon EC2 MapReduce兼容。
答案 0 :(得分:7)
您必须实施自己的输入格式。您也可以定义自己的记录阅读器。
不幸的是你必须定义一个getSplits()方法。在我看来,这将比实现记录阅读器更难:这种方法必须实现一个逻辑来输入数据。
请参阅以下摘录“Hadoop - 权威指南”(我一直推荐的一本好书!):
这是界面:
public interface InputFormat<K, V> {
InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
RecordReader<K, V> getRecordReader(InputSplit split,
JobConf job,
Reporter reporter) throws IOException;
}
JobClient调用getSplits()方法,传递所需数量的map任务 作为numSplits参数。这个数字被视为一个提示,因为InputFormat实现了 心理可以自由地将不同数量的分割返回到指定的数量 numSplits。在计算了拆分后,客户端将它们发送到jobtracker,即 使用他们的存储位置来安排地图任务,以便在任务工具上处理它们。
在任务跟踪器上,map任务将拆分传递给getRecordReader()方法 InputFormat获取该分割的RecordReader。 RecordReader只不过是 记录上的迭代器,map任务使用一个来生成记录键值对, 它传递给地图功能。代码段(基于MapRunner中的代码) 说明了这个想法:
K key = reader.createKey();
V value = reader.createValue();
while (reader.next(key, value)) {
mapper.map(key, value, output, reporter);
}
答案 1 :(得分:2)
我最近解决了这个问题,只需创建我自己的InputFormat来覆盖NLineInputFormat并实现自定义的MultiLineRecordReader而不是默认的LineReader。
我选择扩展NLineInputFormat,因为我想保证每次拆分都有N行。
此记录阅读器几乎与http://bigdatacircus.com/2012/08/01/wordcount-with-custom-record-reader-of-textinputformat/
一样我修改的唯一内容是现在使用新API的maxLineLength
的属性,以及从NLineInputFormat的NLINESTOPROCESS
内部硬编码读取的setNumLinesPerSplit()
的值(更多信息)柔韧性)。
结果如下:
public class MultiLineInputFormat extends NLineInputFormat{
@Override
public RecordReader<LongWritable, Text> createRecordReader(InputSplit genericSplit, TaskAttemptContext context) {
context.setStatus(genericSplit.toString());
return new MultiLineRecordReader();
}
public static class MultiLineRecordReader extends RecordReader<LongWritable, Text>{
private int NLINESTOPROCESS;
private LineReader in;
private LongWritable key;
private Text value = new Text();
private long start =0;
private long end =0;
private long pos =0;
private int maxLineLength;
@Override
public void close() throws IOException {
if (in != null) {
in.close();
}
}
@Override
public LongWritable getCurrentKey() throws IOException,InterruptedException {
return key;
}
@Override
public Text getCurrentValue() throws IOException, InterruptedException {
return value;
}
@Override
public float getProgress() throws IOException, InterruptedException {
if (start == end) {
return 0.0f;
}
else {
return Math.min(1.0f, (pos - start) / (float)(end - start));
}
}
@Override
public void initialize(InputSplit genericSplit, TaskAttemptContext context)throws IOException, InterruptedException {
NLINESTOPROCESS = getNumLinesPerSplit(context);
FileSplit split = (FileSplit) genericSplit;
final Path file = split.getPath();
Configuration conf = context.getConfiguration();
this.maxLineLength = conf.getInt("mapreduce.input.linerecordreader.line.maxlength",Integer.MAX_VALUE);
FileSystem fs = file.getFileSystem(conf);
start = split.getStart();
end= start + split.getLength();
boolean skipFirstLine = false;
FSDataInputStream filein = fs.open(split.getPath());
if (start != 0){
skipFirstLine = true;
--start;
filein.seek(start);
}
in = new LineReader(filein,conf);
if(skipFirstLine){
start += in.readLine(new Text(),0,(int)Math.min((long)Integer.MAX_VALUE, end - start));
}
this.pos = start;
}
@Override
public boolean nextKeyValue() throws IOException, InterruptedException {
if (key == null) {
key = new LongWritable();
}
key.set(pos);
if (value == null) {
value = new Text();
}
value.clear();
final Text endline = new Text("\n");
int newSize = 0;
for(int i=0;i<NLINESTOPROCESS;i++){
Text v = new Text();
while (pos < end) {
newSize = in.readLine(v, maxLineLength,Math.max((int)Math.min(Integer.MAX_VALUE, end-pos),maxLineLength));
value.append(v.getBytes(),0, v.getLength());
value.append(endline.getBytes(),0, endline.getLength());
if (newSize == 0) {
break;
}
pos += newSize;
if (newSize < maxLineLength) {
break;
}
}
}
if (newSize == 0) {
key = null;
value = null;
return false;
} else {
return true;
}
}
}
}
答案 2 :(得分:1)
我认为在你的情况下你可以遵循委托模式并实现一个围绕LineRecordReader的包装器,它覆盖必要的方法,即next()(或新API中的nextKeyValue()),将值设置为N行的串联,而不是一条线。
我用google搜索ParagraphRecordReader的示例性实现,它使用LineRecordReader逐行读取输入数据(并连接它),直到遇到EOF或空行。然后它返回pair,其中value是段落(而不是一行)。此外,ParagraphRecordReader的ParagraphInputFormat与标准TextInputFormat一样简单。
您可以找到此实施的必要链接以及以下帖子的几个词:http://hadoop-mapreduce.blogspot.com/2011/03/little-more-complicated-recordreaders.html。
最佳