1、掌握MapReduce并行编程方法
2、掌握自定义数据类型
3、掌握自定义分区类和自定义排序类的使用
4、掌握最值求解并行化方法
假设有一个服务器每天都记录同一个网站的访问量数据,主要是该网站下所有页面中的最大访问量和最小访问量,数据存储在下面三个文件中。
说明:第一列为某年某月的时间信息,第二列为该月内某天观测到的最大访问量,第三列为该月内同一天观测到的最小访问量。
程序设计要求如下:
最后输出网站每个月内的最大值、最小值,一个月一行数据。
如图中2017-07最大值为900,最小值为100;2017-08最大值为560,最小值为200
输出格式如下
2017-08 560 200
2017-07 900 100
必须自定义一个数据类型,包含某天观测到的最大最小访问量。
要求自定义分区函数,2017年的数据全部规约到一个reducer处理,2018年的数据全部规约到另一个reducer处理。
要求同一年的数据按月份降序排序。如
2017-08 560 200
2017-07 900 100
完成本次实验需要有一定的设计代码思维,因为有两个年份,先按照年月份进行降序排序,然后引用Partitioner函数把年份分成两份文件输出。最后排序出每个月份的最大销量和最小销量进行输出。
1、 创建一个类MyWritable,定义一个最大值和最小值
public class MyWritable implements Writable{
private int maxAccess;
private int minAccess;
@Override
public void readFields(DataInput in) throws IOException {
// TODO Auto-generated method stub
maxAccess=in.readInt();
minAccess=in.readInt();
}
@Override
public void write(DataOutput out) throws IOException {
// TODO Auto-generated method stub
out.writeInt(maxAccess);
out.writeInt(minAccess);
}
@Override
public String toString() {
return maxAccess + " " + minAccess;
}
public int getMaxAccess() {
return maxAccess;
}
public int getMinAccess() {
return minAccess;
}
public void setMaxAccess(int maxAccess) {
this.maxAccess = maxAccess;
}
public void setMinAccess(int minAccess) {
this.minAccess = minAccess;
}
}
2、创建一个yearMonthOrder类 比较年月份大小,降序排。
public class yearMonthOrder extends WritableComparator{
public yearMonthOrder() {
// TODO Auto-generated constructor stub
super(Text.class,true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
// TODO Auto-generated method stub
Text v1=(Text) a;
Text v2=(Text) b;
//比较V1和V2的大小,降序排。
return -super.compare(v1, v2);
}
}
3、创建一个MyPartitioner类,引用Partitioner 函数进行排序
public class MyPartitioner extends Partitioner<Text, MyWritable>{
@Override
public int getPartition(Text key, MyWritable value, int arg2) {
return Integer.parseInt(key.toString().substring(0, 4))%arg2;
}
}
4、创建一个Mapper类
public class MyMapper extends Mapper<LongWritable, Text, Text, MyWritable> {
MyWritable MW=new MyWritable();
//在map函数中将年月作为key值
public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException {
String[] serverAccess = ivalue.toString().split(" ");
MW.setMaxAccess(Integer.parseInt(serverAccess[1]));
MW.setMinAccess(Integer.parseInt(serverAccess[2]));
context.write(new Text(serverAccess[0]),MW);
}
}
5、创建MyReduce,在reduce函数中比较每个月的销量,按照从高到低排序
public class MyReducer extends Reducer<Text, MyWritable, Text, MyWritable> {
private MyWritable jieguo = new MyWritable();
public void reduce(Text _key, Iterable<MyWritable> values, Context context)
throws IOException, InterruptedException {
// process values
boolean mark=true;
//在reduce函数中比较每个月的销量,按照从高到低排序。
for (MyWritable val : values) {
if(mark){
jieguo.setMaxAccess(val.getMaxAccess());
jieguo.setMinAccess(val.getMinAccess());
mark=false;
}else{
if(val.getMaxAccess()>jieguo.getMaxAccess()){
jieguo.setMaxAccess(val.getMaxAccess());
}
if(val.getMinAccess()<jieguo.getMinAccess()){
jieguo.setMinAccess(val.getMinAccess());
}
}
}
context.write(_key, jieguo);
}
}
6、创建一个Driver类,进行数据处理
public class MyDriver {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
//修改key和value之间的分隔符为空格
conf.set("mapred.textoutputformat.separator", " ");
Job job = Job.getInstance(conf, "JobName");
job.setJarByClass(shiyan3.MyDriver.class);
// TODO: specify a mapper
job.setMapperClass(MyMapper.class);
// TODO: specify a reducer
job.setReducerClass(MyReducer.class);
// TODO: specify output types
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(MyWritable.class);
//1、自定义Partition类,因为一年有2个年 ,因此需要2个分区,指定Partition类,以及partition的数量。
job.setNumReduceTasks(2);
job.setPartitionerClass(MyPartitioner.class);
job.setSortComparatorClass(yearMonthOrder.class);
// TODO: specify input and output DIRECTORIES (not files)
FileInputFormat.setInputPaths(job, new Path("hdfs://localhost:9000/shiyan3/input"));
FileOutputFormat.setOutputPath(job, new Path("hdfs://localhost:9000/shiyan3/output"));
if (!job.waitForCompletion(true))
return;
}
}