- MapReduce
- WordCount单词统计
- WordCount源码分析
- 参考
MapReduce
MapReduce是一种可用于数据处理的编程模型。它的任务过程分为两个处理阶段: map 阶段和 reduce 阶段。每阶段都以 键-值对 作为输入和输出,其类型由我们按需选择。我们还需要写两个函数: map 函数和 reduce 函数。
map 函数由Mapper类来表示,后者声明一个抽象的 map() 方法。Mapper类是一个泛型类型,它有四个形参类型,分别指定 map 函数的输入键、输入值、输出键、输出值的类型。
同样, reduce 函数也有四个形式参数类型用于指定输入和输出类型。 reduce 函数的输入类型必须匹配 map 函数的输出类型。
WordCount单词统计
首先有这么一个文件,文件内容如下:
hello world hello java
hello hadoop
那么hadoop是怎么做单词统计的呢?我们用步骤来描述下:
- 第一步:读取这个文件,按行来将这个文件每一行的单词给拆分出来,然后形成很多key/value的结果,处理完就是这样
<hello,1>
<world,1>
<hello,1>
<java,1>
<hello,1>
<hadoop,1> - 第二步:排序
排序完会变成这样的结果
<hadoop,1>
<hello,1>
<hello,1>
<hello,1>
<java,1>
<world,1> - 第三步:合并
合并后的结果如下
<hadoop,1>
<hello,1,1,1>
<java,1>
<world,1> - 第四步:汇聚结果
<hadoop,1>
<hello,3>
<java,1>
<world,1>
第二步和第三步是hadoop框架帮助我们完成的,我们实际上需要写代码的地方是第一步和第四步。 第一步对应的就是Map的过程,第四步对应的是Reduce的过程。
WordCount源码分析
import java.io.IOException;
import java.util.StringTokenizer;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.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 WordCount {// TokenizerMapper作为Map阶段,需要继承Mapper,并重写map()函数public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{//这个泛型声明告诉MapReduce框架,Mapper的输入是不加限制的通用对象和文本,而输出是文本和整数private final static IntWritable one = new IntWritable(1);//IntWritable 是 Hadoop 提供的用于表示整数的数据类型。这里是为每个单词设置一个计数,表示单词在文本中出现的次数private Text word = new Text();public void map(Object key, Text value, Context context) throws IOException, InterruptedException {// 用StringTokenizer作为分词器,对value进行分词StringTokenizer itr = new StringTokenizer(value.toString());//默认情况下使用空格作为分隔符。// 遍历分词后结果while (itr.hasMoreTokens()) {// itr.nextToken() 逐个获取单词。每个String类型的单词都会被设置到 Text 类型的 word 变量中word.set(itr.nextToken());// 将(word,1),即(Text,IntWritable)写入上下文context,供后续Reduce阶段使用context.write(word, one);}}}// IntSumReducer作为Reduce阶段,需要继承Reducer,并重写reduce()函数public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {private IntWritable result = new IntWritable();//reduce方法对每个键(单词)的值列表进行迭代,累加计算单词出现的总次数,并将结果输出为(word, total_count)。public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {int sum = 0;// 遍历map阶段输出结果中的values中每个val,累加至sumfor (IntWritable val : values) {sum += val.get();}// 将sum设置入IntWritable类型resultresult.set(sum);// 通过上下文context的write()方法,输出结果(key, result),即(Text,IntWritable)context.write(key, result);}}public static void main(String[] args) throws Exception {// 加载hadoop配置Configuration conf = new Configuration();// 校验命令行输入参数,确保用户提供了输入路径和输出路径if (args.length < 2) {System.err.println("Usage: wordcount <in> [<in>...] <out>");//要求用户提供输入路径(可能是多个),然后一个输出路径。System.exit(2);//在这里,退出码是 2,通常表示程序由于错误的使用方式而被终止}// 构造一个Job实例job,并命名为"word count"Job job = new Job(conf, "word count");// 设置jar,指定运行该作业的Jar文件。Hadoop利用方法中的类来查找包含它的JAR文件,进而找到相关的JAR文件。job.setJarByClass(WordCount.class);// 设置Mapperjob.setMapperClass(TokenizerMapper.class);// 设置Combinerjob.setCombinerClass(IntSumReducer.class);// 设置Reducerjob.setReducerClass(IntSumReducer.class);// 设置OutputKeyjob.setOutputKeyClass(Text.class);// 设置OutputValuejob.setOutputValueClass(IntWritable.class);// 添加输入路径。通过循环,将所有的输入路径添加到作业的配置中for (int i = 0; i < args.length - 1; ++i) {FileInputFormat.addInputPath(job, new Path(args[i]));}// 添加输出路径。通常是HDFS上的一个目录FileOutputFormat.setOutputPath(job,new Path(args[args.length - 1]));// 等待作业job运行完成并退出System.exit(job.waitForCompletion(true) ? 0 : 1);}
}
参考
https://juejin.cn/post/6844903942183190541