前两篇中,我们了解了MapReduce的执行流程及其架构实现,今天我们就在本地通过经典实例WordCount来了解一下MapReduce的编程实现
YARN集群配置
-
首先我们按照大数据之四 hadoop HDFS HA 高可用的完全分布式搭建好集群环境,jps检查无误
-
关闭所有节点上的HDFS相关进程
stop-dfs.sh
- 配置mapred-site.xml文件(
cp mapred-site.xml.template mapred-site.xml)
<!-- MapReduce的架构体系,这里使用MapReduceV2,即YARN -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
- 配置yarn-site.xml文件
<!-- NodeManager上运行的附属服务。需配置成mapreduce_shuffle,才可运行MapReduce程序 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value> <!-- 启用RM的高可用 -->
</property>
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>cluster1</value> <!-- YARN对外提供的服务的id -->
</property>
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value> <!-- 实现RM高可用的节点id -->
</property>
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>node01</value> <!-- rm1对应的真实节点 -->
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>node02</value> <!-- rm2对应的真实节点 -->
</property>
<!--配置三台zookeeper的位置信息 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>node02:2181,node03:2181,node04:2181</value>
</property>
- 将配置完成的hadoop安装包发送到其他节点,覆盖之前的安装包,也可以先删除之前的安装包
scp -r hadoop-2.6.5 root@node02:/opt/zgl/
scp -r hadoop-2.6.5 root@node03:/opt/zgl
scp -r hadoop-2.6.5 root@node04:/opt/zgl/
- 在node01 或 node02 上启动HDFS
start-dfs.sh
- 在node01 或 node02 上启动YARN,这里我们在node01上启动
satrt-yarn.sh
因为 HDFS 和 YARN 都是hadoop的组件之一,这里步骤6,7可以用 start-all.sh 代替
- 启动后使用jps查看,成功的话node01节点上会启动ResourceManager进程,node02,node03,node04上会启动NodeManager进程。
[root@node01 ~]# jps
2390 NameNode
2726 DFSZKFailoverController
3111 Jps
2839 ResourceManager
2584 JournalNode
[root@node02 ~]# jps
2834 Jps
2706 NodeManager
2487 JournalNode
2393 DataNode
2571 DFSZKFailoverController
2268 QuorumPeerMain
2333 NameNode
- 这里我们会发现Node02上并没有启动standby状态的RM,这是因为系统默认就是不启动的,这里我们可以手动将其启动
yarn-daemon.sh start resourcemanager
[root@node02 ~]# jps
3008 ResourceManager
2706 NodeManager
2487 JournalNode
2393 DataNode
2571 DFSZKFailoverController
2268 QuorumPeerMain
2333 NameNode
3055 Jps
- YARN也提供Web UI来显示job相关信息,浏览器输入node01:8088。后面我们运行WordCount时,就可以在这里查看job状态

eclipse配置
更新客户端的hadoop安装包,按照 大数据之五 hadoop HDFS HA集群客户端+eclipse(java API)配置 对eclipse进行配置,确认其可以操作HDFS
打开Windows->Show View中的Map/Reduce Locations,右键选择Edit Hadoop location…

在下图对MapReduce进行配置(确保node02上已启动RM)

新建一个java项目,将hadoop安装包中share\hadoop目录下 common,hdfs,tools,yarn,mapreduce五个文件夹中的jar包及其下lib文件夹中的jar包整合导入项目并build path
将mapred-site.xml,yarn-site.xml文件拷贝到source文件夹中

接下来,我们就可以开始写代码了
WordCount
我们先来回顾一下MapReduce处理大数据集的过程

做一些准备工作
首先我们在本地创建两个文本文件file1.txt和file2.txt,使file1.txt内容为"Hello World\nBye World",file2.txt的内容为"Hello Hadoop\nBye Hadoop"。
在HDFS 上创建输入文件夹 input,上传本地文件到集群的input 目录下
再来分析一下WordCount的执行过程
- 将文件拆分成splits,由于测试用的文件较小,所以我们写两个文件,每个文件为一个split,并将文件按行分割形成<key,value>对。这一步由MapReduce框架自动完成,其中偏移量(即key值)包括了回车所占的字节数

- 将分割好的<key,value>对交给用户定义的map方法进行处理,生成新的<k2,v2>对

- 得到map方法输出的<key,value>对后,Mapper会将它们按照key值进行排序分组,并执行Combine,将key至相同value值累加,得到Mapper的最终输出结果

- Reducer先对从Mapper接收的数据进行排序,再交由用户自定义的reduce方法进行处理,得到新的<k3,v3>对,并作为WordCount的输出结果

分析完执行过程,我们来看一下代码实现。
要编程实现MapReduce计算,只需要继承Mapper类实现其map()方法,继承Reduce类实现其reduce()方法,并在main()函数中对job进行设置
序列化
因为HDFS涉及到序列化的问题,Hadoop的基本数据类型都实现了一个Writable接口,而实现了这个接口的类型都支持序列化。在Mapper类和Reducer类中都使用Hadoop自带的基本数据类型

map()
/**
* @param KEYIN
* →k1 表示每一行的起始位置(偏移量offset) LongWritable
* @param VALUEIN
* →v1 表示每一行的文本内容 Text
* @param KEYOUT
* →k2 表示每一行中的每个单词 Text
* @param VALUEOUT
* →v2 表示每一行中的每个单词的出现次数,固定值为1 IntWritable
*/
public class WCMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
Text myKey = new Text(); //设置Text类型的对象,用来封装KEYOUT,即k2
IntWritable myValue = new IntWritable(1); //设置IntWritable类型的对象并固定其值为1,即v2
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
System.out.println(key+"=========="+value); //输出<k1,v1>值,这里的key为offset
//使用hadoop自带的工具类StringUtil进行分词(按空格进行切割),相当于value.toString().split(" ")
String[] words = StringUtils.split(value.toString(), ' ');
//遍历分完的词,即k2,产生<k2,v2>,使用context进行输出。context是个上下文对象
for (String word : words) {
myKey.set(word);
context.write(myKey,myValue);
}
}
}
reduce()
/**
* KEYIN 即K2 表示行中出现的单词 Text
* VALUEIN 即V2 表示出现的单词的次数 IntWritable
* KEYOUT 即K3 表示行中出现的不同单词 Text
* VALUEOUT 即V3 表示行中出现的不同单词的总次数 IntWritable
*/
public class WCReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
//因为reduce处理的是“相同”key的一组数据,这里的key是真的相同,所以只要遍历v2累加计数即可
int sum = 0;
for (IntWritable value : values) {
sum += value.get();
}
//输出<k3,v3>
context.write(key, new IntWritable(sum));
}
}
main()
public class WC {
public static void main(String[] args) throws IOException,ClassNotFoundException, InterruptedException {
//读取MapReduce配置信息,包括HDFS。设置为true则从本地项目source文件夹中读取配置文件
Configuration conf = new Configuration(true);
//构建一个job并指定job名称
Job job = Job.getInstance(conf,"WordCount");
//设置当前main函数所在类
job.setJarByClass(WC.class);
//设置本地jar包位置 (第三种运行模式使用,另两种不用配置。运行模式在下面介绍)
job.setJar("d:/mapreduce/wc.jar");
//设置输入路径 args[0]表示参数手动输入
FileInputFormat.setInputPaths(job, args[0]);
//设置输出路径 这里路径写死,先判断路径下是否有文件,有则删除
Path outputPath = new Path("/output/wordcount");
FileSystem fs = outputPath.getFileSystem(conf);
if(fs.exists(outputPath)){
fs.delete(outputPath,true);
}
FileOutputFormat.setOutputPath(job, outputPath);
//设置Map class
job.setMapperClass(WCMapper.class);
//设置map输出key、value的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//设置reduce class
job.setReducerClass(WCReduce.class);
//设置reduce输出key、value的类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置reduce task的个数
job.setNumReduceTasks(2);
//提交作业
job.waitForCompletion(true);
}
}
main函数中主要做了如下几件事:
一是构建job,指定main函数所在类,指定输入、输出目录;
二是指定自定义的Mapper类和Reducer类及其对应输入输出key,value的类型;
三是提交作业
接下来我们就可以运行代码,运行前我们先来看几种运行模式
运行模式
1、local(在本地的eclipse上启动多个线程来模拟map task,reduce task执行,并未启动集群,所以YARN的job web UI页面不会有任务显示) ,用于测试环境
修改mapred-site.xml 中的mapreduce.framework.name,设置为local
<property>
<name>mapreduce.framework.name</name>
<value>local</value>
</property>
2、提交到集群中运行,用于生产环境
在本地将代码打成jar包,提交到集群。在集群上执行hadoop jar + jar包路径 + main()所在类的全类名 + 参数运行
[root@node01 mapreduce]# ls
wc.jar
[root@node01 mapreduce]# hadoop jar ./wc.jar com.hpe.mr.wc.WC /input/*
3、在本机上的eclipse中直接提交任务到集群中运行,这里我们使用这种方式
(1) 修改配置文件 mapred-site.xml
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.app-submission.cross-platform</name>
<value>true</value> <!-- 跨平台提交开启 -->
</property>
(2) 将本地Application打成jar包,放在window下某一个位置,这里放在D盘的mapreduce文件夹中




(3)右击项目,Run as --> Run Configurations

(4)点击Arguments,在Program arguments中填写参数,即我们在main()中写的args[0]的值,点击运行

(5)我们在console窗口可以看到我们在map中定义的<k1,v1>的输出
0==========hello hadoop
13==========bye hadoop
0==========hello world
12==========bye world`
(6)在node01:8088页面可以看到任务状态

(7)在HDFS上查看output/wordcount

因为在main()中我们设置启动两个reduce task,所以最后输出两个文件part-r-00000和part-r-00001。
这里 bye world , world后面我不小心多加了个 ` ,所以最后结果是这样
本文介绍了如何在YARN集群上配置并运行Hadoop MapReduce的经典案例WordCount。首先,详细讲述了YARN集群的配置步骤,然后在eclipse中配置MapReduce环境,接着详细解析了WordCount的map()、reduce()和main()方法的实现。最后,讨论了MapReduce的运行模式,包括本地运行和集群提交,并展示了如何在YARN上查看作业状态及HDFS上的输出结果。

1951

被折叠的 条评论
为什么被折叠?



