ページの例のようにMultipleOutputs
クラスを使用しようとしました http://hadoop.Apache.org/docs/mapreduce/r0.21.0/api/index.html?org/Apache /hadoop/mapreduce/lib/output/MultipleOutputs.html
ドライバーコード
Configuration conf = new Configuration();
Job job = new Job(conf, "Wordcount");
job.setJarByClass(WordCount.class);
job.setInputFormatClass(TextInputFormat.class);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
MultipleOutputs.addNamedOutput(job, "text", TextOutputFormat.class,
Text.class, IntWritable.class);
System.exit(job.waitForCompletion(true) ? 0 : 1);
レデューサーコード
public class WordCountReducer extends
Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
private MultipleOutputs<Text, IntWritable> mos;
public void setup(Context context){
mos = new MultipleOutputs<Text, IntWritable>(context);
}
public void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
//context.write(key, result);
mos.write("text", key,result);
}
public void cleanup(Context context) {
try {
mos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
レデューサーの出力の名前がtext-r-00000に変更されていることがわかります
しかし、ここでの問題は、空のpart-r-00000ファイルも取得していることです。これはMultipleOutputsの動作が期待される方法ですか、それともコードに問題がありますか?アドバイスをお願いします。
私が試したもう1つの方法は、FileSystemクラスを使用して出力フォルダーを反復処理し、partで始まるすべてのファイルの名前を手動で変更することです。
最良の方法は何ですか?
FileSystem hdfs = FileSystem.get(configuration);
FileStatus fs[] = hdfs.listStatus(new Path(outputPath));
for (FileStatus aFile : fs) {
if (aFile.isDir()) {
hdfs.delete(aFile.getPath(), true);
// delete all directories and sub-directories (if any) in the output directory
}
else {
if (aFile.getPath().getName().contains("_"))
hdfs.delete(aFile.getPath(), true);
// delete all log files and the _SUCCESS file in the output directory
else {
hdfs.rename(aFile.getPath(), new Path(myCustomName));
}
}
MultipleOutputs
を使用している場合でも、デフォルトのOutputFormat
(TextOutputFormat
だと思います)が引き続き使用されているため、これらのpart-r-xxxxx
が初期化および作成されます。あなたが見ているファイル。
それらが空であるという事実は、MultipleOutputs
を使用しているため、context.write
を実行していないためです。ただし、初期化中に作成されるのを防ぐことはできません。
それらを取り除くには、OutputFormat
を定義して、出力を期待していないことを示す必要があります。あなたはこのようにそれを行うことができます:
job.setOutputFormat(NullOutputFormat.class);
このプロパティを設定すると、パーツファイルがまったく初期化されないようになりますが、出力はMultipleOutputs
で取得されます。
おそらくLazyOutputFormat
を使用することもできます。これにより、出力ファイルは、データがある場合にのみ作成され、空のファイルは初期化されません。あなたは私をこのようにすることができます:
import org.Apache.hadoop.mapreduce.lib.output.LazyOutputFormat;
LazyOutputFormat.setOutputFormatClass(job, TextOutputFormat.class);
Reducer
でプロトタイプMultipleOutputs.write(String namedOutput, K key, V value)
を使用していることに注意してください。これは、namedOutput
に基づいて生成されるデフォルトの出力パスを次のように使用するだけです。{namedOutput}-(m|r)-{part-number}
。出力ファイル名をより細かく制御したい場合は、プロトタイプMultipleOutputs.write(String namedOutput, K key, V value, String baseOutputPath)
を使用する必要があります。これにより、キー/値に基づいて実行時に生成されたファイル名を取得できます。
これは、出力ファイルのベース名を変更するためにDriverクラスで行う必要があるすべてです。job.getConfiguration().set("mapreduce.output.basename", "text");
したがって、これにより、ファイルは「text-r-00000」と呼ばれるようになります。