MapReduce入门之wordcount案例
1 pom.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast.bigdata</groupId>
<artifactId>example-mr</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.7.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>cn.itcast.mr.wordcount.WordCountClient</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2 WordCountMapper类
package cn.itcast.mr.wordcount;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.io.Serializable;
/**
* @description: 此类就是MapReduce程序中mapper阶段业务逻辑处理的类 也就是maptask
* @author: Allen Woon
* @time: 2020/5/20 14:18
* todo KEYIN, VALUEIN, KEYOUT, VALUEOUT
* KEYIN: mapper输入kv中k的数据类型 在默认组件下 是每行起始偏移量 long
* VALUEIN: mapper输入kv中v的数据类型 在默认组件下 是每行内容 String
* KEYOUT: mapper输出kv中k的数据类型 本需求中 是单词 String
* VALUEOUT: mapper输出kv中v的数据类型 本需求中 是单词的次数1 long
* todo mapreduce使用默认的组件TextInputFormat读取数据,行为:一行一行的读数据。
* k:这一行起始位置的偏移量 offset
* v:这一行内容
*
* todo String long等数据类型是java的数据类型。在网络传递数据的时候需要序列化
* todo 但是hadoop认为java序列化机制()效率不高 甚至觉得臃肿。因此hadoop自己封装了序列化机制(Writable) 并且提供了相关的数据类型
*
* java hadoop(org.apache.hadoop.io)
* long-----> longWritable
* String---> Text
* double---> DoubleWritable
* null-----> nullWritable
*/
public class WordCountMapper extends Mapper<LongWritable, Text,Text,LongWritable>{
/**
*todo map方法就是mapper阶段具体业务逻辑处理的方法
*todo TextInputFormat读取一行数据 封装成一个kv对 调用一次map方法。 读一行调用一次处理一次
*/
@Override //<0,hello tom hello> ----> <hello,1> <tom,1> <hello,1>
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//把value转换成为String类型 也就是这一行的数据内容
String line = value.toString();//hello tom hello
//根据分隔符切割改行数据内容
String[] words = line.split("\\s+");// [hello ,tom ,hello]
//遍历数组 每个单词都标记1
for (String word : words) {
//todo 使用MapReduce程序封装的上下文对象context 把数据写出去
context.write(new Text(word),new LongWritable(1)); //<hello,1> <tom,1> <hello,1>
}
}
}
3 WordCountReducer
package cn.itcast.mr.wordcount;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* @description: 此类就是MapReduce程序中reducer阶段业务逻辑处理的类 也就是reducetask
* @author: Allen Woon
* @time: 2020/5/20 15:14
*
* KEYIN,VALUEIN,KEYOUT,VALUEOUT
*
* KEYIN:表示的是reducer阶段输入kv中k的类型 也就是mapper输出中k的类型。 本需求中就是单词 Text
* VALUEIN:表示的是reducer阶段输入kv中v的类型 也就是mapper输出中v的类型。 本需求中就是单词次数1 longWritable
* KEYOUT: 表示的是reducer阶段输出kv中k的类型 本需求中是单词 Text
* VALUEOUT 表示的是reducer阶段输出kv中v的类型 本需求中是单词总次数 longWritable
*/
public class WordCountReducer extends Reducer<Text, LongWritable,Text,LongWritable> {
/**
*todo mapper阶段输出的所有数据来到reduce阶段到底是如何处理的呢?
* <hello,1> <tom,1> <hello,1><tom,1> <hello,1>
*
* todo 1、排序 key的字典序进行排序 a-z 0-9
* <hello,1><hello,1><hello,1><tom,1><tom,1>
* todo 2、分组 key相同的分为一组
* <hello,1><hello,1><hello,1>
* <tom,1><tom,1>
* todo 3、同一组的数据组成一个新的kv对 一组去调用一次reduce方法。
* todo:新k:这组共同的key 新v:这一组所有的value组成的一个迭代器Iterable
* <hello,1><hello,1><hello,1>----> <hello,[1,1,1]>
* <tom,1><tom,1>-----------------> <tom,[1,1]>
* <zip,1> -----------------------> <zip,[1]>
*/
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
//定义个统计变量
long count =0; //<hello,[1,1,1]>
//遍历vlues的迭代器 取值累计求和
for (LongWritable value : values) {
count += value.get();
}
//todo 使用上下文对象Context 把数据写出去
context.write(key,new LongWritable(count)); // <hello,3>
}
}
4WordCountClient
package cn.itcast.mr.wordcount;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
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;
/**
* @description: 该类就是MapReduce程序运行时主类 用于各种参数属性的指定设置 任务提交动作
* @author: Allen Woon
* @time: 2020/5/20 15:37
*/
public class WordCountClient {
public static void main(String[] args) throws Exception{
//配置文件对象
Configuration conf = new Configuration();
//指定MapReduce程序运行的模式
conf.set("mapreduce.framework.name","local");
//创建job对象实例 用于属性封装 任务的提交
Job job = Job.getInstance(conf, "WordCount");
//指定本次mr程序运行的主类
job.setJarByClass(WordCountClient.class);
//指定本次mr运行的mapper类 reducer类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
//指定mapper阶段输出的key value的数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//指定reducer阶段输出的key value的数据类型 也就是整个MapReduce程序最终输出的数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//todo 设置reducetask个数
// job.setNumReduceTasks(3);
//指定本次mr程序输入 输出的数据路径
FileInputFormat.setInputPaths(job,new Path("D:\\datasets\\wordcount\\input"));
FileOutputFormat.setOutputPath(job,new Path("D:\\datasets\\wordcount\\output5"));
//todo 提交job submit()提交之后 客户端就和执行的程序断开了链接 无法实时获取程序执行的情况
// job.submit();
boolean result = job.waitForCompletion(true);// true表示开启监控并打印程序执行的信息
//根据返回的结果 决定程序如何退出 如果执行程序 正常状态码0退出 否则1异常退出。
System.exit(result ? 0 :1);
}
}