实验内容
假设有一个服务器每天都记录同一个网站的访问量数据,主要是该网站下所有页面中的最大访问量和最小访问量,数据存储在下面三个文件中。
数据格式如下(记录时不具体到天):
说明:第一列为某年某月的时间信息,第二列为该月内某天观测到的最大访问量,第三列为该月内同一天观测到的最小访问量。
程序设计要求如下:
- 最后输出网站每个月内的最大值、最小值,一个月一行数据。
如图中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
实验结果
一、代码部分
(一)自定义分区函数
package ann.MaxAndMIn;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
public class MyPartitioner extends Partitioner<Text, MyWritable> {
@Override
public int getPartition(Text key, MyWritable values, int numReduceTasks) {
// TODO Auto-generated method stub
String[] strs = key.toString().split("-");
String strYear = strs[0].toString();
if(strYear.equals("2017")){
return 0;
}else{
return 1;
}
}
}
(二)自定义降序排序
package ann.MaxAndMIn;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;```
import org.apache.hadoop.io.WritableComparator;
public class MySort extends WritableComparator{
public MySort() {
super(Text.class,true);
}
public int compare(WritableComparable a,WritableComparable b) {
Text v1=(Text)a;
Text v2=(Text)b;
return v2.compareTo(v1);
}
}
(二)自定义数据类型
package ann.MaxAndMIn;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
public class MyWritable implements Writable {
private int MaxCount;
private int MinCount;
public int getMaxCount() {
return MaxCount;
}
public void setMaxCount(int maxCount) {
MaxCount = maxCount;
}
public int getMinCount() {
return MinCount;
}
public void setMinCount(int minCount) {
MinCount = minCount;
}
@Override
public void readFields(DataInput in) throws IOException {
// TODO Auto-generated method stub
Text text = new Text();
text.readFields(in);
String[] keys = text.toString().split("\t");
MaxCount = Integer.parseInt(keys[0]);
MinCount = Integer.parseInt(keys[1]);
}
@Override
public void write(DataOutput out) throws IOException {
// TODO Auto-generated method stub
Text text = new Text(MaxCount+"\t"+MinCount);
text.write(out);
}
@Override
public String toString() {
return MaxCount+"\t"+MinCount;
}
}
(三)MapReducer处理
package ann.MaxAndMIn;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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 MaxAndMin {
public static class MaxMinMapper extends Mapper<Object, Text, Text,
MyWritable> {
private MyWritable w = new MyWritable();
public void map(Object key, Text value, Context context)throws IOException, InterruptedException {
String[] strs = value.toString().split(" ");
String strDate = strs[0];// 定义记录日期的字符串变量strDate
if (strDate == null) {
return;// 如果该日期值为空,则返回
}
w.setMaxCount(Integer.parseInt(strs[1]));
w.setMinCount(Integer.parseInt(strs[1]));
context.write(new Text(strDate),w);
}
}
public static class MaxMinReducer extends Reducer<Text, MyWritable, Text,MyWritable> {
private MyWritable r = new MyWritable();
public void reduce(Text key, Iterable<MyWritable> values, Context context)throws IOException, InterruptedException {
boolean flag=true;
//在reduce函数中比较每个月的销量,按照从高到低排序。
for (MyWritable val : values) {
if(flag){
r.setMaxCount(val.getMaxCount());
r.setMinCount(val.getMinCount());
flag = false;
}else{
if(val.getMaxCount()>r.getMaxCount()){
r.setMaxCount(val.getMaxCount());
}
if(val.getMinCount()<r.getMinCount()){
r.setMinCount(val.getMinCount());
}
}
}
context.write(key, r);
}
}
public static void main(String[] args) throws Exception {
String inputpath="hdfs://localhost:9000/Dinput";
String outputpath="hdfs://localhost:9000/Doutput";
args = new String[] {inputpath,outputpath};
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(MaxAndMin.class);
job.setMapperClass(MaxMinMapper.class);
job.setCombinerClass(MaxMinReducer.class);
job.setReducerClass(MaxMinReducer.class);
//设置分区排序job
job.setPartitionerClass(MyPartitioner.class);
job.setNumReduceTasks(2);
job.setSortComparatorClass(MySort.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(MyWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0:1);
}
}
二、终端部分
(一)建立文件夹Dinput
(二)查看该文件夹以及上次实验所需的文件
(三)查看是否上传成功
(四)查看文件的内容是否对
① 文件1
② 文件2
③ 文件3
(五)查看Doutput下的文件
① 2017年的数据
② 2018年的数据
实验结论
(1)这次的实验首先处理的一个问题便是数据的切分,上次实验我用到的是StringTokenizer,这次我用了split函数,原理和相似又不同,split是切分成数组形式,每个值各站一个位置,可以通过数组来调用;然后是把数放到一个自定义的数据类型中MyWritable。
(2)这次实验最大难点便是这个自定义的数据类型,由于最大最小值是一起的,因此在MyWritable读取数据时,不能再是readint这样了,需要再次在MyWritable里面切分一次数值,再赋予max和min;还有注意write里面·也要相对应,否则会出现读取文件和数据越界异常。
(3)在mapper中把数据切分后,通过传到自定义分区函数中分为两个去,一个是2017年的数据,另一个是2018年的数据。再传到reducer进行一次最大最小值比较,再进行按月份降序排序。
(4)对于进行降序排序时,我开始是想把text切分,用月份进行比较,而且想在sort类中加入一个变量,但由于无法处理变量的关系,最后没有成功。
(5)开始上传实验所需的文件时,文件3的数据是空的。经过删除对比最后才上传完整的数据。因此,无论在做什么事情时,都需要仔细,认真查看。