Hadoop关于预处理和后处理作业的链接提供了两种解决方案。
输出结果如下:
方案一:为预处理和后处理步骤各自编写一个MapReduce作业,并将其链接起来。在这些步骤中可以使用IdentityReducer。而在此过程中每一个步骤的中间结果都需要占用I/O和存储资源,所以这种方案是非常低效的,一般情况下Hadoop是不建议使用的。
方案二:自己组合mapper和reducer,也就是说自己写mapper去预先调用所有的预处理步骤,再让reducer调用所有的后处理步骤。在Hadoop中呢,是引入了ChainMapper和ChainReducer类来简化预处理和后处理的构成,生成的作业表达式类似于:MAP+ | REDUCE | MAP+ 通过按照这个顺序来执行执行多个mapper来预处理数据,并在reduce之后可选的地按序执行多个mapper来做数据的后处理。
例如:有4个mapper作业和一个reduce作业,顺序如下:
Map1 | Map2| Reduce | Map3 | Map4
2012-3-1 a
2012-3-2 b
2012-3-3 c
2012-3-4 d
2012-3-5 a
2012-3-6 b
2012-3-7 c
2012-3-3 c
2012-3-1 b
2012-3-2 a
2012-3-3 b
2012-3-4 d
2012-3-5 a
2012-3-6 c
2012-3-7 d
2012-3-3 c
package chain;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapreduce.lib.chain.ChainMapper;
import org.apache.hadoop.mapreduce.lib.chain.ChainReducer;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
public class Chain {
public static class Mapper1 extends Mapper<Object,Text,Text,Text>{
public void map(Object key,Text value,Context context)throws IOException,InterruptedException{
context.write(value, new Text(" "));
}
}
public static class Mapper2 extends Mapper<Text,Text,Text,Text>{
public void map(Text key,Text value,Context context)throws IOException,InterruptedException{
String string = key.toString().split(" ")[0];
String val = key.toString().split(" ")[1];
context.write(new Text(string), new Text(val));
}
}
public static class Reduce extends Reducer<Text,Text,Text,Text>{
public void reduce(Text key,Iterable<Text> values,Context context)throws IOException,InterruptedException{
String [] string = key.toString().split("-");
StringBuffer str = new StringBuffer();
str.append(string[0]).append("年").append(string[1]).append("月").append(string[2]).append("日");
for (Text val:values){
context.write(new Text(str.toString()), val);
}
}
public static void main(String [] args)throws IOException,InterruptedException, ClassNotFoundException{
Configuration conf = new Configuration();
Job job = new Job(conf,"chain");
job.setJarByClass(Chain.class);
JobConf mapper1conf = new JobConf(false);
ChainMapper.addMapper(job, Mapper1.class, LongWritable.class, Text.class, Text.class, Text.class, mapper1conf);
JobConf mapper2conf = new JobConf(false);
ChainMapper.addMapper(job, Mapper2.class, Text.class, Text.class, Text.class, Text.class, mapper2conf);
JobConf reduceconf = new JobConf(false);
ChainReducer.setReducer(job, Reduce.class, Text.class, Text.class, Text.class, Text.class, reduceconf);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
job.setNumReduceTasks(1);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true)?0:1);
}
}
}
输出结果如下:
2012年3月3日 c
2012年3月7日 c
2012年3月6日 b
2012年3月5日 a
2012年3月4日 d
2012年3月3日 c
2012年3月2日 b
2012年3月1日 a
2012年3月3日 c
2012年3月7日 d
2012年3月6日 c
2012年3月5日 a
2012年3月4日 d
2012年3月3日 b
2012年3月2日 a
2012年3月1日 b