经典案例:mapreduce统计单词词频
前置准备
①hadoop集群
②idea
③Xshell
1. 首先在Windows上打开编程工具idea,建立maven工程(此步骤较为简单,不做详细介绍)。
2. 在建立的工程下找到并打开pom.xml,将以下代码粘贴进去,粘贴完成后导入依赖
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.7.7</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></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>
3.在java下建立cn.itcast.example.mr包,在包下建立如下图所示的三个类
4.在WordCountMapper类中编写如下map阶段的代码
package cn.itcast.example.mr;
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<KEYIN,VALUEIN,KEYOUT,VALUEOUT>
KEYIN:表示mapper阶段数据输入的时候key的数据类型,在默认的读取数据组件下,叫InputFormat,它的行为是一行一行地读取待处理的数据
读取一行,返回一行给我们的mr程序,在这种情况下 keyin 就表示每一行的起始偏移量,因此数据偏移量是Long
VALUEIN:表示mapper阶段数据输入的时候value的数据类型,在默认的读取数据组件下 valuein 就表示读取的这一行内容,因此数据类型是String
KEYOUT:表示mapper阶段数据输出的时候key的数据类型,在本案例中,输出的key是单词,因此数据类型是String
VALUEOUT:表示mapper数据输出的时候value的数据类型,在本案例中,输出的key是单词的次数,因此数据类型是Integer
这里所说的数据类型String Long都是jdk自带的类型,在序列化的时候效率低下,因此hadoop自己封装了一套数据类型
long---->LongWritable
String---->Text
Integer---->Intwritable
null---->NullWritable
*/
public class WordCountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
/*
这里就是mapper阶段具体的业务逻辑实现方法,该方法的调用取决于读取数据的组件有没有给mr传入数据
如果有,每传入一个<k,v>对,该方法就会被调用一次
*/
protected void map(LongWritable key,Text value,Context context) throws IOException,InterruptedException{
// 拿到传入进来的一行内容,把数据类型转化为String
String line = value.toString();
// 将这一行内容按照分隔符进行切割,切割成一个单词数组
String words[] = line.split(" ");
// 遍历数组,每出现一个单词,就标记数字1
for (String word : words){
// 使用mr程序的上下文context,把mapper阶段处理的数据发送出去
// 作为reduce阶段的输入数据
context.write(new Text(word),new IntWritable(1));
}
}
}
5.在WordCountReducer类中编写reduce阶段的代码
package cn.itcast.example.mr;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/*
KEYIN:就是reducer阶段输入的数据key类型,对应mapper阶段的输出key类型,在本案例中就是单词Text
VALUEIN:就是reducer阶段输入的数据value类型,对应mapper阶段的输出value类型,在本案例中就是单词次数 IntWritable
KEYOUT:就是reducer阶段输出的数据key类型,在本案例中就是单词Text
VALUEOUT:就是reducer阶段输出的数据value类型,在本案例中就是单词总次数IntWritable
*/
public class WordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
/*
按照key是否相同作为一组去调用reduce方法
本方法的key就是这一组相同kv对的共同key
把这一组所有的v作为一个迭代器传入我们的reduce方法
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
// 定义一个计数器
int count = 0;
// 遍历一组迭代器,把每一个数量1累加起来就构成了单词的总次数
for (IntWritable value:values) {
count += value.get();
}
// 把最终的结果输出
context.write(key,new IntWritable(count));
}
}
6.在WordCountDriver中编写主类的代码
package cn.itcast.example.mr;
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;
/*
这个类就是mr程序运行时候的主类,本类中组装了一些程序运行时所需要的信息
比如:使用的是哪个mapper类,哪个reducer类,输入数据在哪,输出数据在哪
*/
public class WordCountDriver {
public static void main(String[] args)throws Exception{
// 通过Job来封装本次mr的相关信息
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
// 指定本次mr job jar包运行主类
job.setJarByClass(WordCountDriver.class);
// 指定本次mr,所用的mapper,reducer类分别是什么
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
// 指定本次mr,mapper阶段的输出k,v类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
// 指定本次mr最终输出的k,v类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 指定本次mr输入的数据路径和最终输出结果存放在什么位置
FileInputFormat.setInputPaths(job,"/wordcount/input");
FileOutputFormat.setOutputPath(job,new Path("/wordcount/output"));
// job.submit();
// 提交程序,并且监控打印程序执行情况
boolean b = job.waitForCompletion(true);
System.exit(b?0:1);
}
}
7.将编写好的代码打成jar包
7.1 复制主类的路径
7.2 将复制的路径粘贴到pom.xml的如下位置
7.3 打成jar包
点击右上角的Maven,出现右边对话框,双击package,稍作等待
之后会在左边出现如下target包,箭头所指即为jar包
8.运行jar包
8.1 上传jar包
打开VMware,并用Xshell连接,启动hadoop集群,将刚才打包的example-mr-1.0.jar包拖入Xshell,并查看(如图所示)
8.2 创建数据
在xk用户下分别创建 1.txt 和 2.txt ,写入如下数据(因为我没有数据,所以就自己构造了)
#单词之间以空格分隔
[xk@hadoop01 ~]$ vi 1.txt
hadoop spark hello hello
spark kylin flume
flume kylin
[xk@hadoop01 ~]$ vi 2.txt
hadoop spark kylin hello sqoop
spark hadoop hadoop sqoop
8.3 在hdfs上创建如下文件夹
[xk@hadoop01 ~]$ hadoop fs -mkdir -p /wordcount/input
8.4 将1.txt和2.txt上传到/wordcount/input
[xk@hadoop01 ~]$ hadoop fs -put 1.txt 2.txt /wordcount/input
8.5 运行jar
[xk@hadoop01 ~]$ hadoop jar example-mr-1.0.jar
9.查看运行结果
[xk@hadoop01 ~]$ hadoop fs -ls /wordcount
Found 2 items
drwxr-xr-x - xk supergroup 0 2020-06-27 17:46 /wordcount/input
drwxr-xr-x - xk supergroup 0 2020-06-27 17:48 /wordcount/output
[xk@hadoop01 ~]$ hadoop fs -ls /wordcount/output
Found 2 items
-rw-r--r-- 3 xk supergroup 0 2020-06-27 17:48 /wordcount/output/_SUCCESS
-rw-r--r-- 3 xk supergroup 49 2020-06-27 17:48 /wordcount/output/part-r-00000
[xk@hadoop01 ~]$ hadoop fs -cat /wordcount/output/part-r-00000
flume 2
hadoop 4
hello 3
kylin 3
spark 4
sqoop 2