一、實驗目的及要求
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;
}
}