使用 Hadoop MapReduce Streaming 实现多文件输出

151 阅读2分钟

在使用 Hadoop MapReduce Streaming 进行数据处理时,有时需要将输入的多个文件分别处理,并生成多个输出文件。例如,假设有三个输入文件 one.txttwo.txtthree.txt,需要分别处理它们并生成三个输出文件 one_out.txttwo_out.txtthree_out.txt

huake_00210_.jpg

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() 可以获取输入文件的文件名,然后从输入行中解析数据,并输出数据。

最终,将输出数据写入到指定的输出路径中,每个输入文件将对应一个输出文件。