MapReduce之自定义InputFormat
背景: 在企业开发中,Hadoop框架自带的InputFormat类型不能满足所有应用场景,需要自定义InputFormat来解决实际问题。
自定义InputFormat步骤如下:
- 自定义一个类继承FilelnputFormat。
- 自定义一个类继承RecordReader,实现一次读取一个完整文件,将文件名为key,文件内容为value。
- 在输出时使用SequenceFileOutPutFormat输出合并文件。
无论HDFS还是MapReduce,在处理小文件时效率都非常低,但又难免面临处理大量小文件的场景,此时,就需要有相应解决方案。可以自定义InputFormat实现小文件的合并。
需求
将多个小文件合并成一个SequenceFile文件(SequenceFile文件是Hadoop用来存储二进制形式的key-value(bytes) 对的文件格式),SequenceFile里面存储着多个文件,存储的形式为文件路径+名称为key,文件内容为value。
-
输入数据
-
期望输出文件格式
需求分析
自定义一个类继承FileInputFormat
- 重写isSplitable()方法,返回false,让文件不可切,整个文件作为1片。
- 重写createRecordReader(),返回自定义的RecordReader对象
自定义一个类继承RecordReader
在RecordReader中,nextKeyValue()是最重要的方法,返回当前读取到的key-value,如果读到返回true,调用Mapper的map()来处理,否则返回false
- 编写程序
MyInputFormat.java
/*
* 1. 改变切片策略,一个文件固定切1片,通过指定文件不可切
* 2. 提供RR ,这个RR读取切片的文件名作为key,读取切片的内容封装到bytes作为value
*/
public class MyInputFormat extends FileInputFormat {
@Override
public RecordReader createRecordReader(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
return new MyRecordReader();
}
@Override
protected boolean isSplitable(JobContext context, Path filename) {
return false;
}
}
MyRecordReader.java
/*
* RecordReader从MapTask处理的当前切片中读取数据
* XXXContext都是Job的上下文,通过XXXContext可以获取Job的配置Configuration对象
*/
public class MyRecordReader extends RecordReader {
private Text key;
private BytesWritable value;
private String filename;
private int length;
private FileSystem fs;
private Path path;
private FSDataIn