MapReduce入门案例之wordcount(计算单词出现次数)
MR的wordcount的练习就相当于java的helloworld一样
学习前提:
-
JAVA基础:数据类型、方法、面向对象、反射等等(看懂语法)
-
maven(处理依赖、打包)
-
Hadoop-HDFS的存储原理(看懂集群架构、block等)
-
Hadoop-MapReduce的执行原理(看懂流程)
-
Linux (shell操作)
一共要定义3个类,
- Map类(定义Map阶段怎么处理)
- Reduce类(定义Reduce阶段怎么处理)
- JobMain主类(定义整个MapReduce处理流程,见下)
准备数据
hello,world,hadoop
hive,sqoop,flume,hello
kitty,tom,jerry,world
hadoop
保存成.txt文件
MapReduce 处理数据流程:
Map阶段:
- 输入,读取源数据( setInputFormatClass方法 / 得出K1,V1)
- 设置Mapper类( 继承Mapper类 / K1,V1 转换 K2,V2)
shuffle阶段(直接默认,跳过)
- 分区
- 排序
- 规约
- 分组
Reduce阶段
- 设置Reduce类( 继承Reduce类 / 新K2,V2 转换 K3,V3)
- 输出,保存结果( setOutputFormatClass方法 / 输出K3,V3)
代码:
pom.xml 设置远程仓库、依赖、脚本
<!--指定仓库-->
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<!--打包方式-->
<packaging>jar</packaging>
<!--包-->
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.0-mr1-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Mapper类
1.必须继承Mapper类并且指定好K1,V1,K2,V2对应的hadoop数据类型
2.必须重写map方法
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/*
KEYIN:K1的类型
VALUEIN:V1的类型
KEYOUT:K2的类型
VALUEOUT:V2的类型
注意:
Mapper泛型里面要用hadoop自定义的类型(其实就是Hadoop将原本的类型加上序列化操作再封装)
即org.apache.hadoop包下的数据类型
如:long -> LongWritable;
String-> Text
...
*/
public class WordCountMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
//目的:将K1,V1 转换 K2,V2
/*
参数:
key :K1 行偏移量
value : V1 每一行的文本数据
context:上下文对象,桥梁,连接shuffle阶段
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//创建对象保存数据
LongWritable ValueOut = new LongWritable();
Text KeyOut = new Text();
//1:拆分文本数据
String[] split = value.toString().split(",");
//2:遍历数据,拆分,重组装K2 ,V2
for (String word : split) {
KeyOut.set(word);
ValueOut.set(1);
//3:将K2,V2写入上下文对象当中
context.write(KeyOut, ValueOut);
}
}
}
//完毕
Reduce
(同Map类似)
1.必须继承Reducer并指定K2,V2,K3,V3的类型
2.重写reduce方法
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
//虽然第二个参数即V2是集合,但是仍然使用集合里面的元素类型作为泛型参数
public class WordCountReduce extends Reducer<Text, LongWritable, Text, LongWritable> {
//目的:新K2,V2 转成K3,V3,将K3,V3写入上下文
/*
该类方法实现的结果如下:
要处理的数据:
新 K2 V2
Hello <1,1,1>
-----------------
最终输出结果:
K3 V3
hello 3
*/
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
long count = 0;
//1:遍历集合,将集合中的数字相加,得到V3
for (LongWritable value : values) {
count += value.get();
}
//2:将K3和V3写入上下文
context.write(key, new LongWritable(count));
}
}
主类JobMain
(依照8个流程步骤编写代码)
1.必须继承Configured类、实现Tool接口
2.必须重写run方法
3.main方法来启动程序
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
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.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.net.URI;
//MapReduce需要定义主类来描述job并提交job,用来启动MR-Job
//关键点1:必须要继承 Configured 配置类,和实现 Tool 接口,注意是hadoop包下的
public class JobMain extends Configured implements Tool {
//关键点2:必须重写一个run方法来调用
@Override
public int run(String[] strings) throws Exception {
Job job = Job.getInstance(super.getConf(), JobMain.class.getSimpleName());
//打包到集群上面运行时候,必须要添加以下配置,指定程序的main函数
//如果打包出错就需要加上该配置
job.setJarByClass(JobMain.class);
//第一步:设置输入类型,读取路径,读取输入文件解析成键值对K1,V1
job.setInputFormatClass(TextInputFormat.class);
//集群做好hosts地址映射,不用直接些IP,写node01即可
TextInputFormat.addInputPath(job, new Path("hdfs://node01:8020/wordcount"));//自动读取文件夹所有的文件
//本地运行(必须配置本地hadoop环境)
/*TextInputFormat.addInputPath(job,
new Path("file:///F:\\mapreduce\\mrinput\\wordcount.txt"));*/
//第二步:设置Mapper类,并设置Map阶段完成之后的输出类型(K2,V2)
job.setMapperClass(WordCountMapper.class);//class是反射的知识
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//第三、四、五、六步,默认,暂时不用写
//第七步:设置Reduce类,并设置Reduce阶段完成之后的输出类型(K3,V3)
job.setReducerClass(WordCountReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//第八步:设置输出类型和输出路径
job.setOutputFormatClass(TextOutputFormat.class);
Path path = new Path("hdfs://node01:8020/wordcount_out");
//本地运行(必须配置本地hadoop环境)
/*TextOutputFormat.setOutputPath(job,
new Path("file:///F:\\mapreduce\\mroutput"));*/
TextOutputFormat.setOutputPath(job, path);//如果目录已存在会报错
//改良:避免目录已存在,先判断是否存在,存在就删除
//连接HDFS文件系统
//获取FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"),
new Configuration());
//判断目录是否存在
boolean hbl = fileSystem.exists(path);
if (hbl) {
//删除目录 第一个参数是删除哪个目录 第二个参数是否递归删除
fileSystem.delete(path, true);
}
//等待MR程序完成..
boolean mrb = job.waitForCompletion(true);
//返回run主类的执行结果
return mrb ? 0 : 1;//三元运算符
}
//关键点3:由于是主启动函数,需要创建main主函数
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
//本地执行,可以加入配置参数
//configuration.set("mapreduce.framework.name", "local");
//configuration.set("yarn.resourcemanager.hostname", "local");
Tool tool = new JobMain();
int run = ToolRunner.run(configuration, tool, args);
System.exit(run);
}
}
最后,打包,上次,执行
集群运行:
#hadoop jar 编写好的MR程序Jar包 主方法所在的类
hadoop jar original-mapreducedemo-1.0-SNAPSHOT.jar com.yh.mapreduce.JobMain
本地运行需要的环境:
- 下载解压windows版Hadoop
- 环境变量HADOOP_HOME
- Path:%HADOOP_HOME%\bin
- 复制bin目录下的hadoop.dll到c:\system32目录下
- 重启
End!~

浙公网安备 33010602011771号