案例描述
根据用户行为数据,编写 MapReduce 程序来统计出商品点击量排行。
案例要求
根据提示,在右侧编辑器补充代码,计算得出商品点击量排行。
- main 方法已给出,其中 Job 和输入输出路径已配置完成,无需更改;
- map 和 reduce 的输入输出 key、value 已给出;
- 编程中直接写 map 与 reduce 过程的主要内容即可。
排序的概述
-
在 MapReduce 的 Shuffle 的过程中执行了三次排序,分别是: map 中的溢写阶段:根据分区以及 key 进行快速排序。 map 中合并溢写文件:将同一分区的多个溢写文件进行归并排序,合成一个大的溢写文件。 reduce 输入阶段:将同一分区,来自不同 map task 的数据文件进行归并排序。
-
在 MapReduce 整个过程中,默认是会对输出的键值对按照 key 进行排序的,而且是使用快速排序。map 输出的排序的,其实也就是上面的溢写过程中的排序。reduce 输出的排序,即 reduce 处理完数据后,MapReduce内部会自动对输出的键值对按照key进行排序。
代码实现
package educoder;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
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;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class ItemClickRankDriver {
public static class ThisMap extends Mapper<Object, Text, Text, IntWritable> {
private static IntWritable one = new IntWritable(1);
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String[] atts = value.toString().split(",");
String item = atts[1];
String behavior = atts[3];
if (behavior.equals("pv")) {
context.write(new Text(item), one);
}
}
}
public static class ThisReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
List<Object[]> list = new LinkedList<>();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
for (IntWritable one : values) {
sum += one.get();
}
list.add(new Object[] { key.toString(), Integer.valueOf(sum) });
}
@Override
protected void cleanup(Reducer<Text, IntWritable, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
list = list.stream().sorted((o1, o2) -> { return ((int)o1[1] - (int)o2[1]);}).collect(Collectors.toList());
for(int i=list.size()-1; i>=0; i--){
Object[] o = list.get(i);
context.write(new Text((String) o[0]), new IntWritable((int) o[1]));
}
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "商品点击量排行");
job.setJarByClass(ItemClickRankDriver.class);
job.setMapperClass(ThisMap.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setReducerClass(ThisReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}