一:背景
Hadoop2.0开始MapReduce作业支持链式处理,类似于富士康生产苹果手机的流水线,每一个阶段都有特定的任务要处理,比如提供原配件——>组装——打印出厂日期,等等。通过这样进一步的分工,从而提高了生成效率,我们Hadoop中的链式MapReduce也是如此,这些Mapper可以像水流一样,一级一级向后处理,有点类似于Linux的管道。前一个Mapper的输出结果直接可以作为下一个Mapper的输入,形成一个流水线。
注:链式MapReduce的执行规则:整个Job中只能有一个Reducer,在Reducer前面可以有一个或者多个Mapper,在Reducer的后面可以有0个或者多个Mapper。
二:技术实现
#需求:现有如下销售数据,要求使用链式MapReduce,在第一个Mapper中过滤金额大于10000的数据,在第二个Mapper中过滤数据在100-10000之间的数据,在Reduce中进行分类汇总,在Reduce后面的Mapper中过滤掉商品名长度大于8的数据。
-
Phone 5000
-
Computer 2000
-
Clothes 300
-
XieZi 1200
-
QunZi 434
-
ShouTao 12
-
Books 12510
-
SmallShangPing 5
-
SmallShangPing 3
-
DingCan 2
实现代码:
-
public class ChainMapReduce {
-
-
// 定义输入输出路径
-
private static final String INPUTPATH = "hdfs://liaozhongmin21:8020/chainFiles/*";
-
private static final String OUTPUTPATH = "hdfs://liaozhongmin21:8020/out";
-
-
public static void main(String[] args) {
-
-
try {
-
-
Configuration conf = new Configuration();
-
// 创建文件系统
-
FileSystem fileSystem = FileSystem.get( new URI(OUTPUTPATH), conf);
-
-
// 判断输出路径是否存在,如果存在则删除
-
if (fileSystem.exists( new Path(OUTPUTPATH))) {
-
fileSystem.delete( new Path(OUTPUTPATH), true);
-
}
-
-
// 创建Job
-
Job job = new Job(conf, ChainMapReduce.class.getSimpleName());
-
-
// 设置输入目录
-
FileInputFormat.addInputPath(job, new Path(INPUTPATH));
-
// 设置输入文件格式
-
job.setInputFormatClass(TextInputFormat.class);
-
-
// 设置自定义的Mapper类
-
ChainMapper.addMapper(job, FilterMapper1.class, LongWritable.class, Text.class, Text.class, DoubleWritable.class, conf);
-
ChainMapper.addMapper(job, FilterMapper2.class, Text.class, DoubleWritable.class, Text.class, DoubleWritable.class, conf);
-
ChainReducer.setReducer(job, SumReducer.class, Text.class, DoubleWritable.class, Text.class, DoubleWritable.class, conf);
-
// 注:Reducer后面的Mapper也要用ChainReducer进行加载
-
ChainReducer.addMapper(job, FilterMapper3.class, Text.class, DoubleWritable.class, Text.class, DoubleWritable.class, conf);
-
-
// 设置自定义Mapper类的输出key和value
-
job.setMapOutputKeyClass(Text.class);
-
job.setMapOutputValueClass(DoubleWritable.class);
-
-
// 设置分区
-
job.setPartitionerClass(HashPartitioner.class);
-
// 设置reducer数量
-
job.setNumReduceTasks( 1);
-
-
// 设置自定义的Reducer类
-
// 设置输出的Key和value类型
-
job.setOutputKeyClass(Text.class);
-
job.setOutputValueClass(DoubleWritable.class);
-
-
// 设置输出路径
-
FileOutputFormat.setOutputPath(job, new Path(OUTPUTPATH));
-
// 设置输出格式
-
job.setOutputFormatClass(TextOutputFormat.class);
-
-
// 提交任务
-
System.exit(job.waitForCompletion( true) ? 0 : 1);
-
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
-
-
-
/**
-
* 过滤掉金额大于10000的记录
-
* @author 廖钟民 2015年3月17日下午6:27:05
-
*/
-
public static class FilterMapper1 extends Mapper<LongWritable, Text, Text, DoubleWritable> {
-
-
// 定义输出的key和value
-
private Text outKey = new Text();
-
private DoubleWritable outValue = new DoubleWritable();
-
-
-
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, DoubleWritable>.Context context) throws IOException,
-
InterruptedException {
-
-
// 获取行文本内容
-
String line = value.toString();
-
-
if (line.length() > 0) {
-
// 对行文本内容进行切分
-
String[] splits = line.split( "\t");
-
// 获取money
-
double money = Double.parseDouble(splits[ 1].trim());
-
// 过滤
-
if (money <= 10000) {
-
// 设置合法结果
-
outKey.set(splits[ 0]);
-
outValue.set(money);
-
// 把合法结果写出去
-
context.write(outKey, outValue);
-
}
-
}
-
}
-
}
-
-
/**
-
* 过滤掉金额大于100的记录
-
* @author 廖钟民 2015年3月17日下午6:29:27
-
*/
-
public static class FilterMapper2 extends Mapper<Text, DoubleWritable, Text, DoubleWritable> {
-
-
-
protected void map(Text key, DoubleWritable value, Mapper<Text, DoubleWritable, Text, DoubleWritable>.Context context) throws IOException,
-
InterruptedException {
-
if (value.get() < 100) {
-
-
// 把结果写出去
-
context.write(key, value);
-
}
-
}
-
}
-
-
/**
-
* 金额汇总
-
* @author 廖钟民
-
*2015年3月21日下午1:46:47
-
*/
-
public static class SumReducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable> {
-
// 定义输出的value
-
private DoubleWritable outValue = new DoubleWritable();
-
-
-
protected void reduce(Text key, Iterable<DoubleWritable> values, Reducer<Text, DoubleWritable, Text, DoubleWritable>.Context context)
-
throws IOException, InterruptedException {
-
-
// 定义汇总结果
-
double sum = 0;
-
// 遍历结果集进行统计
-
for (DoubleWritable val : values) {
-
-
sum += val.get();
-
}
-
// 设置输出value
-
outValue.set(sum);
-
// 把结果写出去
-
context.write(key, outValue);
-
}
-
}
-
-
/**
-
* 过滤商品名称长度小于8的商品
-
* @author 廖钟民
-
*2015年3月21日下午1:47:01
-
*/
-
public static class FilterMapper3 extends Mapper<Text, DoubleWritable, Text, DoubleWritable> {
-
-
-
protected void map(Text key, DoubleWritable value, Mapper<Text, DoubleWritable, Text, DoubleWritable>.Context context) throws IOException,
-
InterruptedException {
-
// 过滤
-
if (key.toString().length() < 8) {
-
// 把结果写出去
-
System.out.println( "写出去的内容为:" + key.toString() + "++++"+ value.toString());
-
context.write(key, value);
-
}
-
}
-
-
}
-
-
}
-
-
-
输出结果: