簡介
上一章介紹了MR的wordcount的demo,這一節我們來進行擴充,逐步將wordcount豐富,擁有更多的業務基礎邏輯。
我們目標
- 是實現自主切分partition,生成自定義的數量和數據特徵的partition文件。
- 並且實現序列化數據bean,利用writableComparable接口實現升序降序排列。
首先我們看一眼數據,這是我隨便敲的。
我們用partition分成兩個文件,分別是奇數和偶數。
並且每個文件降序排序。
得到如下
文件一:
文件二:
目錄結構
和上一章相比我們增加了兩個文件
dataBean 和partitioner
dataBean
package MRcode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @Author: Braylon
* @Date: 2020/1/19 10:18
* @Version: 1.0
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class dataBean implements WritableComparable<dataBean> {
private Long data0;
public void set(Long data0) {
this.data0 = data0;
}
@Override
public int compareTo(dataBean o) {
return this.data0 > o.getData0() ? -1 : 1;
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.data0 = dataInput.readLong();
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeLong(data0);
}
@Override
public String toString() {
return '\001' + data0.toString() + '\001';
}
}
這裏我增加了一個dataBean來標記數據,因爲每個數據只有一個數字,所以我就只寫了一個data0,在真正的應用中肯定不止一個屬性。然後需要注意的是,這個bean實現了WritableComparable的接口。
至於爲什麼要使用這個實現自定義數據的序列化,我看了一個博主寫的很好,我借用一下:
- 在進行mapreduce編程時key鍵往往用於分組或排序,當我們在進行這些操作時Hadoop內置的key鍵數據類型不能滿足需求時,
或針對用例優化自定義數據類型可能執行的更好。因此可以通過實現org.apache.hadoop.io.WritableComparable接口定義一個自定義的WritableComparable類型,並使其作爲mapreduce計算的key類型。
2.自定義Hadoop key類型。
1.Hadoop mapreduce的key類型往往用於進行相互比較, 可以達到進行相互比較來滿足排序的目的。
2.Hadoop Writable數據類型實現了WritableComparable接口,並增加了CompareTo()方法。
CompaeTo()方法的返回值有三種類型。負整數、0、正整數分別對應小於、等於、大於被比較對象
————————————————
原文鏈接:https://blog.csdn.net/xj626852095/article/details/52675874
然後要注意的就是compareTo方法,這個的返回值很關鍵,決定了排序方式。
partitioner
package MRcode;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Partitioner;
/**
* @Author: Braylon
* @Date: 2020/1/18 12:03
* @Version: 1.0
*/
public class partitioner extends Partitioner<dataBean, IntWritable> {
@Override
public int getPartition(dataBean dataBean, IntWritable intWritable, int i) {
String str = dataBean.getData0().toString();
String firWord = str.substring(str.length() - 1, str.length());
char[] charArray = firWord.toCharArray();
int result = charArray[0];
if (result % 2 == 0) {
return 0;
}else {
return 1;
}
}
}
這裏就是根據數據的最後以爲的奇偶來返回到不同的parttion文件中,定義了兩種不同的返回數據我們需要在driver中設定reducenum,如下所示:
driver
package MRcode;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class WorkCountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
args = new String[] {"inputPath","outputPath"};
//獲取配置信息
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(WorkCountDriver.class);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
job.setMapOutputKeyClass(dataBean.class);
job.setMapOutputValueClass(IntWritable.class);
//reduce 輸出的K,V類型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setPartitionerClass(partitioner.class);
job.setNumReduceTasks(2);
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//提交job
job.waitForCompletion(true);
}
}
job.setPartitionerClass(partitioner.class);
job.setNumReduceTasks(2);
上面的兩行是很重要的,尤其是設置reducetask的個數,由於在partitioner中設置了兩種不同的返回類型,所以這裏的個數必須要大於等於2,否則會報錯。
下面我放上mapper和reducer類的代碼,其中比較重要的是他們的輸入輸出類型,具體的map和reduce的過程與原理大家可以看上一章我寫的,這裏不再贅述。
mapper
package MRcode;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class WordCountMapper extends Mapper<LongWritable, Text, dataBean, IntWritable> {
dataBean k = new dataBean();
IntWritable v = new IntWritable();
@Override
protected void map(LongWritable key,
Text value,
Context context) throws IOException, InterruptedException {
//第一步 轉化格式
String line0 = value.toString();
//第二部 切分數據 按空格切分數據
String[] words = line0.split(" ");
//第三步 輸出<單詞 , 1>
for (String word : words) {
//寫法1:context.write(new Text(word),new IntWritable(1));
k.set(Long.parseLong(word));
v.set(1);
context.write(k,v);
}
}
}
reducer
package MRcode;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordCountReducer extends Reducer<dataBean, IntWritable,Text,IntWritable> {
@Override
protected void reduce(dataBean key,
Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
//初始化次數
int count=0;
//彙總次數
for(IntWritable value : values){
count += value.get();
}
//輸出總次數
context.write(new Text(key.getData0().toString()), new IntWritable(count));
}
}
最終得到上述的結果。