在使用 Hadoop MapReduce Streaming 进行数据处理时,有时需要将输入的多个文件分别处理,并生成多个输出文件。例如,假设有三个输入文件 one.txt、two.txt 和 three.txt,需要分别处理它们并生成三个输出文件 one_out.txt、two_out.txt 和 three_out.txt。
2、解决方案
要实现多文件输出,可以采用以下解决方案:
- 如果输入文件的尺寸较小,可以使用
FileInputFormat,Hadoop 将为每个文件生成一个单独的 mapper 任务,从而最终为每个输入文件生成一个输出文件(如果不需要 reducer)。 - 如果输入文件较大,则需要编写自定义的输入格式,并将
isSplittable(false)设为 false。这将确保 Hadoop 不会将文件跨多个 mapper 分割,也不会为每个输入文件生成多个输出文件。
以下是一个使用自定义输入格式实现多文件输出的代码示例:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
public class MultiFileOutput {
public static class MyMapper extends Mapper<NullWritable, Text, NullWritable, Text> {
@Override
protected void map(NullWritable key, Text value, Context context) throws IOException, InterruptedException {
// 获取输入文件名
String inputFileName = context.getInputSplit().toString().split("/")[context.getInputSplit().toString().split("/").length - 1];
// 从输入行中解析数据
String[] data = value.toString().split(",");
// 输出数据,第一列为输入文件名,第二列为数据
context.write(NullWritable.get(), new Text(inputFileName + "," + data[0]));
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
// 配置作业
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "MultiFileOutput");
job.setJarByClass(MultiFileOutput.class);
// 设置输入输出格式
job.setInputFormatClass(CustomInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
// 设置 mapper 类
job.setMapperClass(MyMapper.class);
// 设置输出键值类型
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
// 设置输入输出路径
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 提交作业
job.waitForCompletion(true);
}
}
在上面的代码中,CustomInputFormat 是自定义的输入格式,它将整个文件作为单个输入记录提供给 mapper。
在 mapper 中,通过 context.getInputSplit().toString() 可以获取输入文件的文件名,然后从输入行中解析数据,并输出数据。
最终,将输出数据写入到指定的输出路径中,每个输入文件将对应一个输出文件。