环境:IDEA、Maven、Hadoop-3.0.0
一、分区简单概述
在进行MapReduce计算时,有时会需要我们把最终的输出数据按照某种规则放到不同的不同的文件中,比如手机号的前三位划分省份,要把同一个省份的数据放到同一个文件中。对于MapReduce的最终输出数据,最终来自于Reducer,如果要得到多个文件,就意味着要有同样多数量的Reducer任务在运行。而Reducer的数据来自于Mapper,也就是说,在Mapper阶段要将划分数据,之后将不同的数据交给不同的Reducer运行。Mapper划分数据的过程便称为分区,而负责实现划分数据的类叫做Partitioner。
二、排序简单概述
排序是MapReduce很重要的一部分,在Mapper和Reducer阶段会默认对key进行排序,如果key属于某个自定义类,且希望key按某种方式进行排序,此时这个自定义类就要实现Java中的Comparable接口,另外这个类还要实现Hadoop的Writable序列化接口,于是,我们便可以直接实现WritableComparable接口。
本例子中,我们要将不同科目的成绩放在不同的文件中,并将成绩按照降序排序,因此,我们需要实现实现WritableComparable接口并继承Partitioner类实现getPartition方法。
原始数据:http://链接:https://pan.baidu.com/s/1jO_NJ_Gs0vuP9yMKHnFMmQ?pwd=yyds 提取码:yyds
下面直接上代码
Wrive类:
需要有以下步骤:
1、定义类实现writableComparable接口
2、重写空参构造
3、重写序列化与反序列化方法
4、重写compareTo方法
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Wrive implements WritableComparable<Wrive> {
private int score;
public Wrive() {
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public String toString(){
return(this.score+" ");
}
@Override
public int compareTo(Wrive o) {
if(this.score>o.score){
return -1;
}
else if(this.score<o.score){
return 1;
}else{return 0;}
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeInt(this.score);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.score=dataInput.readInt();
}
}
Partitioner类:
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
public class partition extends Partitioner<Wrive,Text> {
@Override
public int getPartition(Wrive wrive, Text text, int i) {
String a=text.toString();
if("语文".equals(a)){
return 0;
}else if("数学".equals(a)){
return 1;
}else if("英语".equals(a)){
return 2;
}else if("物理".equals(a)){
return 3;
}else if("化学".equals(a)){
return 4;
}else{
return 5;
}
}
}
注意Partitioner是对Mapper阶段的输出进行的操作,因此此阶段的输入应该是<Wrive,Text>
Mapper类:
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class Map extends Mapper<LongWritable, Text, Wrive, Text> {
Wrive a=new Wrive();
Text b=new Text();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Wrive, Text>.Context context) throws IOException, InterruptedException {
String da=value.toString();
String[] data =da.split(" ");
b.set(data[0]);
a.setScore(Integer.parseInt(data[1]));
context.write(a,b);
}
}
Reducer类:
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class Rduece extends Reducer< Wrive, Text,Text,Wrive> {
@Override
protected void reduce(Wrive key, Iterable<Text> values, Reducer<Wrive, Text, Text, Wrive>.Context context) throws IOException, InterruptedException {
for(Text value:values){
context.write(value, key);
}
}
}
主程序入口:
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
//创建一个job和任务入口
Job job=Job.getInstance(new Configuration());
job.setJarByClass(Main.class);//main方法所在的class
//指定job的mapper和输出的类型
job.setMapperClass(Map.class);
job.setMapOutputKeyClass(Wrive.class);
job.setMapOutputValueClass(Text.class);
//指定job的reducer和输出的类型
job.setReducerClass(Rduece.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Wrive.class);
//指定分区的规则
job.setPartitionerClass(partition.class);
//建立几个分区
job.setNumReduceTasks(6);
//创建job指定的输入和输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}