本文講解利用MR實現簡單的單詞統計功能。
創建com.test.hadoop.wordcount包,然后在包中創建自定義mapper類,自定義reducer類,以及main類。
Mapper代碼
package com.kaikeba.hadoop.wordcount;
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;
/**
* 類Mapper<LongWritable, Text, Text, IntWritable>的四個泛型分別表示
* map方法的輸入的鍵的類型kin、值的類型vin;輸出的鍵的類型kout、輸出的值的類型vout
* kin指的是當前所讀行行首相對于split分片開頭的字節偏移量,所以是long類型,對應序列化類型LongWritable
* vin指的是當前所讀行,類型是String,對應序列化類型Text
* kout根據需求,輸出鍵指的是單詞,類型是String,對應序列化類型是Text
* vout根據需求,輸出值指的是單詞的個數,1,類型是int,對應序列化類型是IntWritable
*
*/
public class WordCountMap extends Mapper<LongWritable, Text, Text, IntWritable> {
/**
* 處理分片split中的每一行的數據;針對每行數據,會調用一次map方法
* 在一次map方法調用時,從一行數據中,獲得一個個單詞word,再將每個單詞word變成鍵值對形式(word, 1)輸出出去
* 輸出的值最終寫到本地磁盤中
* @param key 當前所讀行行首相對于split分片開頭的字節偏移量
* @param value 當前所讀行
* @param context
* @throws IOException
* @throws InterruptedException
*/
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
//當前行的示例數據(單詞間空格分割):Dear Bear River
//取得當前行的數據
String line = value.toString();
//按照\t進行分割,得到當前行所有單詞
String[] words = line.split("\t");
for (String word : words) {
//將每個單詞word變成鍵值對形式(word, 1)輸出出去
//同樣,輸出前,要將kout, vout包裝成對應的可序列化類型,如String對應Text,int對應IntWritable
context.write(new Text(word), new IntWritable(1));
}
}
}
Reducer代碼
package com.kaikeba.hadoop.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
*
* Reducer<Text, IntWritable, Text, IntWritable>的四個泛型分別表示
* reduce方法的輸入的鍵的類型kin、輸入值的類型vin;輸出的鍵的類型kout、輸出的值的類型vout
* 注意:因為map的輸出作為reduce的輸入,所以此處的kin、vin類型分別與map的輸出的鍵類型、值類型相同
* kout根據需求,輸出鍵指的是單詞,類型是String,對應序列化類型是Text
* vout根據需求,輸出值指的是每個單詞的總個數,類型是int,對應序列化類型是IntWritable
*
*/
public class WordCountReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
/**
*
* key相同的一組kv對,會調用一次reduce方法
* 如reduce task匯聚了眾多的鍵值對,有key是hello的鍵值對,也有key是spark的鍵值對,如下
* (hello, 1)
* (hello, 1)
* (hello, 1)
* (hello, 1)
* ...
* (spark, 1)
* (spark, 1)
* (spark, 1)
*
* 其中,key是hello的鍵值對被分成一組;merge成[hello, Iterable(1,1,1,1)],調用一次reduce方法
* 同樣,key是spark的鍵值對被分成一組;merge成[spark, Iterable(1,1,1)],再調用一次reduce方法
*
* @param key 當前組的key
* @param values 當前組中,所有value組成的可迭代集和
* @param context reduce上下文環境對象
* @throws IOException
* @throws InterruptedException
*/
public void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
//定義變量,用于累計當前單詞出現的次數
int sum = 0;
for (IntWritable count : values) {
//從count中獲得值,累加到sum中
sum += count.get();
}
//將單詞、單詞次數,分別作為鍵值對,輸出
context.write(key, new IntWritable(sum));// 輸出最終結果
};
}
Main程序入口
package com.kaikeba.hadoop.wordcount;
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;
/**
*
* MapReduce程序入口
* 注意:
* 導包時,不要導錯了;
* 另外,map\reduce相關的類,使用mapreduce包下的,是新API,如org.apache.hadoop.mapreduce.Job;;
*/
public class WordCountMain {
//若在IDEA中本地執行MR程序,需要將mapred-site.xml中的mapreduce.framework.name值修改成local
//參數 c:/test/README.txt c:/test/wc
public static void main(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
//判斷一下,輸入參數是否是兩個,分別表示輸入路徑、輸出路徑
if (args.length != 2 || args == null) {
System.out.println("please input Path!");
System.exit(0);
}
Configuration configuration = new Configuration();
//configuration.set("mapreduce.framework.name","local");
//告訴程序,要運行的jar包在哪
//configuration.set("mapreduce.job.jar","/home/hadoop/IdeaProjects/Hadoop/target/com.kaikeba.hadoop-1.0-SNAPSHOT.jar");
//調用getInstance方法,生成job實例
Job job = Job.getInstance(configuration, WordCountMain.class.getSimpleName());
//設置job的jar包,如果參數指定的類包含在一個jar包中,則此jar包作為job的jar包; 參數class跟主類在一個工程即可;一般設置成主類
// job.setJarByClass(WordCountMain.class);
job.setJarByClass(WordCountMain.class);
//通過job設置輸入/輸出格式
//MR的默認輸入格式是TextInputFormat,輸出格式是TextOutputFormat;所以下兩行可以注釋掉
// job.setInputFormatClass(TextInputFormat.class);
// job.setOutputFormatClass(TextOutputFormat.class);
//設置輸入/輸出路徑
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//設置處理Map階段的自定義的類
job.setMapperClass(WordCountMap.class);
//設置map combine類,減少網路傳出量
job.setCombinerClass(WordCountReduce.class);
//設置處理Reduce階段的自定義的類
job.setReducerClass(WordCountReduce.class);
//注意:如果map、reduce的輸出的kv對類型一致,直接設置reduce的輸出的kv對就行;如果不一樣,需要分別設置map, reduce的輸出的kv類型
//注意:此處設置的map輸出的key/value類型,一定要與自定義map類輸出的kv對類型一致;否則程序運行報錯
// job.setMapOutputKeyClass(Text.class);
// job.setMapOutputValueClass(IntWritable.class);
//設置reduce task最終輸出key/value的類型
//注意:此處設置的reduce輸出的key/value類型,一定要與自定義reduce類輸出的kv對類型一致;否則程序運行報錯
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 提交作業
job.waitForCompletion(true);
}
}
本地運行
初次運行WordCountMain,先設置main方法參數,根據圖示操作即可
設置參數
設置參數
設置參數
然后在Program arguments輸出參數:c:/test/README.txt c:/test/wc(兩個參數間有一個英文空格,表示兩個參數)
在WordCountMain代碼上,點擊鼠標右鍵,運行程序。
運行