前置知识:
Java > MySQL > JDBC > Maven > Linux > Shell
基础部分:
Hadoop2.x (或 Hadoop3.x) > Zookeeper > Hadoop高可用集群 > HA
进阶部分1:
Hive > Hive高级 > Flume > Kafka > HBase > Azkaban > Maxwell > Canal
进阶部分2:
Scala > Spark > Spark调优 > Git > Flink (Java) > Flink (SQL) > Flink内核源码分析 > Flink性能调优
Hadoop 是一个由 Apache 基金会所开发的分布式系统基础架构。
主要解决海量数据的存储和海量数据的分析计算问题。
广义上来说,Hadoop 通常是指一个更宽泛的概念——Hadoop 生态圈。
Hadoop 优势
1)高可靠性:Hadoop 底层维护多个数据副本,所以即使 Hadoop 某个计算元素或存储出现故障,也不会导致数据的丢失。
2)高扩展性:在集群间分配任务数据,可方便的扩展数以千计的节点。
3)高效性:在 MapReduce 的思想下,Hadoop 是并行工作的,以加快任务处理速度。
4)高容错性:能够自动将失败的任务重新分配
可以看到,Hadoop 的设计初衷和产品优势与大数据 5V 特征相合。
狭义上的 Apache Hadoop 由MapReduce、HDFS、Yarn 等模块组成,下图介绍不同大版本的 Hadoop 的组成差异对比:
Hadoop 1.x 时代,Hadoop中的 MapReduce 同时处理业务逻辑运算和资源的调度,耦合性较大。
Hadoop 2.x 时代,增加了 Yarn。Yarn 只负责资源的调度,MapReduce 只负责运算。
Hadoop 3.x 与 2.x 相比在组成上没有变化。
HDFS (Hadoop Distributed File System) 是一个分布式文件系统。首先它用于存储文件,通过目录树来定位文件;其次它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
HDFS 组成:
1)NameNode (nn):存储文件的元数据,如文件名,文件目录结构,文件属性(生成时间、副本数、文件权限),以及每个文件的块列表和块所在的 DataNode 等。
2)DataNode (dn):在本地文件系统存储文件块数据,以及块数据的校验和。
3)Secondary NameNode (2nn):每隔一段时间对 NameNode 元数据备份。
YARN,全称 Yet Another Resource Negotiator,是一种资源协调者,是 Hadoop 的资源管理器。
YARN 组成:
1)ResourceManager (RM):资源管理器,即整个集群资源(内存、CPU等)的管理器
2)NodeManager (NM):节点管理器,即单个节点服务器资源的管理器
3)ApplicationMaster (AM):应用程序管理器,即单个任务运行的管理器
4)Container:容器,相当一台独立的服务器,里面封装了任务运行所需要的资源,如内存、CPU、磁盘、网络等。
说明:
- 客户端可以有多个
- 集群上可以运行多个ApplicationMaster
- 每个NodeManager上可以有多个Container
MapReduce 将计算过程分为两个阶段:Map 和 Reduce
1)Map 阶段并行处理输入数据
2)Reduce 阶段对 Map 结果进行汇总
单机运行,一般只用于演示案例。
不同点:本地模式的数据存储在本地文件系统中,而伪分布式、完全分布式模式的数据存储在HDFS文件系统中。
单机运行,但是具备Hadop集群的所有功能,用一台服务器模拟分布式环境。
伪分布式集群的部署过程与完全分布式基本相同,不同的是将Namenode、SecondaryNameNode 和 ResourceManager 的主机地址设置为同一台主机,也是唯一一台主机。具体配置见本章第2小节。
多台服务器组成分布式环境,实际生产环境使用。
scp -r $pdir1/$fname1 $user@$host:$pdir2/$fname2
scp: 复制命令
$pdir1/$fname1: 本地文件路径/名称
$user@$host:$pdir2/$fname2: 远程用户@主机:远程文件路径/名称
可选参数:
-r: 递归
rsync -av $pdir1/$fname1 $user@$host:$pdir2/$fname2
rsync: 复制命令
可选参数:
-a: 归档拷贝
-v: 显示同步过程
1)准备三台服务器主机,确保主机之间可以正常通信
ifconfig
查看每台主机的ip地址,修改各主机的 /etc/hosts
文件,添加ip地址、主机名的映射。
# sample
172.17.0.2 master
172.17.0.3 slave1
172.17.0.4 slave2
同时注意关闭防火墙,设置静态IP等。
2)安装 JDK
(1)注意 JDK 与 Hadoop 的版本关系,通用的 JDK 版本为 jdk 8。
(2)配置 JDK 环境变量
vi ~/.bash_profile
在文件尾部添加如下配置:
# JAVA
JAVA_HOME=/xxx/jdk1.8.0_341.jdk/Contents/Home
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME
export PATH
3)安装 Hadoop
(1)不同方式安装
- MacOS 通过 HomeBrew 方式安装
brew install hadoop
- 下载官网安装包方式安装
- 官网最新版本地址
- 官网所有版本地址(包含旧版本)
- 清华镜像地址(只有最新版本)
(2)配置 Hadoop 环境变量:
vi ~/.bash_profile
# Hadoop
HADOOP_HOME=/xxx/hadoop/3.3.4
PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
export HADOOP_HOME
export PATH
相关命令:
ssh-keygen -t rsa
生成公钥
cat id_rsa
查看生成的公钥
ssh-copy-id $hostname
复制公钥到目的主机
1)集群部署规划
注意:NameNode、SecondaryNameNode 和 ResourceManager 不要设置在同一个服务器上
master | slave1 | slave2 | |
---|---|---|---|
HDFS | NameNode DataNode |
- DataNode |
SecondaryNameNode DataNode |
YARN | - NodeManager |
ResourceManager NodeManager |
- NodeManager |
2)配置文件说明
首先进入 hadoop 配置文件路径,cd $HADOOP_HOME/etc/hadoop
需要自定义配置的文件有:core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml 和 workers(hadoop 3.0版本之前为 slaves)。
core-site.xml
<configuration>
<property>
<name>fs.defaultFSname>
<value>hdfs://master:9000value>
property>
<property>
<name>hadoop.tmp.dirname>
<value>file:/usr/local/hadoop/3.3.4/tmpvalue>
property>
<property>
<name>hadoop.http.staticuser.username>
<value>yournamevalue>
property>
configuration>
hdfs-site.xml
<configuration>
<property>
<name>dfs.namenode.http-addressname>
<value>master:9870value>
property>
<property>
<name>dfs.namenode.secondary.http-addressname>
<value>slave2:9868value>
property>
<property>
<name>dfs.replicationname>
<value>1value>
property>
<property>
<name>dfs.permissionsname>
<value>falsevalue>
property>
<property>
<name>dfs.namenode.name.dirname>
<value>file:/usr/local/hadoop/3.3.4/tmp/dfs/namenodevalue>
property>
<property>
<name>dfs.namenode.data.dirname>
<value>file:/usr/local/hadoop/3.3.4/tmp/dfs/datanodevalue>
property>
configuration>
yarn-site.xml
<configuration>
<property>
<name>yarn.nodemanager.aux-servicesname>
<value>mapreduce_shufflevalue>
property>
<property>
<name>yarn.resourcemanager.hostnamename>
<value>slave1value>
property>
<property>
<name>yarn.nodemanager.vmem-check-enabledname>
<value>falsevalue>
property>
<property>
<name>yarn.scheduler.capacity.maximum-am-resource-percentname>
<value>100value>
property>
<property>
<name>yarn.nodemanager.env-whitelistname>
<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOMEvalue>
property>
configuration>
mapred-site.xml
<configuration>
<property>
<name>mapreduce.framework.namename>
<value>yarnvalue>
property>
<property>
<name>yarn.app.mapreduce.am.envname>
<value>HADOOP_MAPRED_HOME=/usr/local/hadoop/3.3.4value>
property>
<property>
<name>mapreduce.map.envname>
<value>HADOOP_MAPRED_HOME=/usr/local/hadoop/3.3.4value>
property>
<property>
<name>mapreduce.reduce.envname>
<value>HADOOP_MAPRED_HOME=/usr/local/hadoop/3.3.4value>
property>
configuration>
更多配置解释可以参考以下文章:
Hadoop配置文件详解
Hadoop官网默认配置
workers 文件
master
slave1
slave2
1)集群首次启动
在 master 节点(即 NameNode 所在节点)格式化NameNode
注意:如果集群不是首次启动但是需要重新格式化时,需要先停止hadoop进程,删除所有节点的data和logs目录,然后再格式化。这样才能保证重新生成的集群id和各个datanode的集群id一致。
hdfs namenode -format
2)启动 HDFS
sbin/start-dfs.sh
3)启动 YARN(最好 ResourceManager 对应的节点上启动)
sbin/start-yarn.sh
4)查看 HDFS 的 NameNode
浏览器访问 http://master:9870
5)查看 YARN 的 ResourceManager
浏览器访问 http://slave1:8088
1)上传文件到HDFS
创建目录:hadoop fs -mkdir /input
上传文件:hadoop fs -put $local-dir/a.txt /input
2)拼接文件
cd $hadoop.tmp.dir/subdir0
cat file1 >> tmp.tar.gz
cat file2 >> tmp.tar.gz
tar -zxvf tmp.tar.gz
3)下载文件
hadoop fs -get $hdfs-dir/fname ./
4)执行 wordcount 程序
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.x.jar wordcount /input /output
1)配置修改
mapred-site.xml 新增配置
<property>
<name>mapreduce.jobhistory.addressname>
<value>master:10020value>
property>
<property>
<name>mapreduce.jobhistory.webapp.addressname>
<value>master:19888value>
property>
2)分发配置
rsync -av mapred-site.xml $user@$host:/xxx/
3)启动历史服务器
mapred --daemon start historyserver
jps
查看是否启动成功
4)查看JobHistory
浏览器访问 http://master:19888
日志聚集:应用运行完成后,将程序的运行日志上传到HDFS系统上,方便用户查看。
注意:配置日志聚集功能后,需要重新启动NodeManager、ResourceManager和HistoryServer。
1)配置 yarn-site.xml
<property>
<name>yarn.log-aggregation-enablename>
<value>truevalue>
property>
<property>
<name>yarn.log.server.urlname>
<value>http://master:19888/jobhistory/logsvalue>
property>
<property>
<name>yarn.log-aggregation.retain-secondsname>
<value>604800value>
property>
2)分发配置
rsync -av yarn-site.xml $user@$host:/xxx/
3)重启 NodeManager、ResourceManager 和 HistoryServer
sbin/stop-yarn.sh
sbin/start-yarn.sh
mapred --daemon stop -historyserver
mapred --daemon start -historyserver
1)各模块分开启动/停止
启动/停止HDFS:start-dfs.sh
/ stop-dfs.sh
启动/停止YARN:start-yarn.sh
/ stop-yarn.sh
2)各服务租金啊足以启动/停止
(1)分别启动停止HDFS组件
hdfs --daemon start/stop namenode/datanode/secondarynamenode
(2)分别启动停止YARN组件
yarn --daemon start/stop resourcemanager/nodemanager
由于数据规模日益增加,单个机器无法容纳,所以采用分布式的方式管理多个机器上的文件,这就是所说的分布式文件系统。
HDFS 就是分布式文件系统的一种,其使用场景为:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后不再需要修改。
- HDFS 的优点
1)高容错性:多副本机制,副本丢失后自动恢复;
2)适合处理大规模数据;
3)构建成本低。- HDFS 的缺点
1)不适合低延时数据访问,比如毫秒级;
2)无法对大量的小文件进行高效存储:因为NameNode的内存有限,并且可能会出现小文件的寻址时间超过文件读取时间;
3)一个文件同时只能由一个进程写,不支持并发写入。并且仅支持数据的追加操作,不支持文件的随机修改。
1)NameNode
(1)管理HDFS的名称空间;
(2)配置副本策略;
(3)管理数据块 (Block) 映射信息;
(4)处理客户端读写请求。
2)DataNode
(1)存储实际的数据块;
(2)执行数据块的读/写操作。
3)Client:客户端
(1)文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个个的 Block,然后进行上传;
(2)与 NameNode 交互,获取文件的位置信息;
(3)与 DataNode 交互,读取或者写入数据;
(4)Client 提供些命令来管理 HDFS,比如 NameNode 格式化;
(5)Client 可以通过一些命令来访问 HDFS,比如对 HDFS 增删查改操作;
4)Secondary NameNode:并非NameNode的热备,NameNode挂掉后不能立即代替NameNode提供服务。
(1)辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode ;
(2)在紧急情况下,可辅助恢复NameNode。
HDFS中的文件在物理上是分块存储的,块的大小可以通过配置参数dfs.blocksize
来规定,Hadoop 2.x/3.x 版本默认大小为128M。
“128M”是怎么确定的?
期望的寻址时间为10ms,理想情况下 block 的寻址时间是传输时间的1%,而目前磁盘的传输速率普遍在100MB/s,因此 块的大小 = 100MB/s x (10ms / 1%) = 100MB。
思考:为什么块的大小不能设置太小,也不能设置太大?
(1)HDFS的块设置太小,会增加寻址时间,程序一直在找块的开始位置;
(2)如果块设置的太大,从磁盘传输前数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。
总结:HDFS 块的大小设置主要取决于磁盘传输速率。
hadoop fs 具体命令
或hdfs fs 具体命令
或dfs fs 具体命令
command | 说明 |
---|---|
-ls |
显示目录信息 |
-cat |
显示文件内容 |
-chgrp 、-chmod 、-chown |
修改文件所属权限 |
-mkdir |
创建文件夹 |
-cp |
从 HDFS 的一个路径拷贝到另一个路径 |
-mv |
在 HDFS 中移动文件 |
-tail |
查看一个文件末尾的1kb数据 |
-rm |
删除一个文件或文件夹 |
-rm -r |
递归删除文件夹及文件夹下的内容 |
-du -s -h |
统计文件夹的大小信息 |
-setrp |
设置 HDFS 中文件的副本数量 |
hadoop fs -mkdir /test
command | 说明 |
---|---|
-moveFromLocal |
从本地剪切粘贴到 HDFS |
-copyFromLocal |
从本地拷贝文件到 HDFS |
-put |
等同于-copyFromLocal |
-appendToFile |
追加一个文件到已经存在的文件末尾 |
hadoop fs -put ./a.txt /test
command | 说明 |
---|---|
-copyToLocal |
从 HDFS 拷贝文件到本地 |
-get |
等同于-copyToLocal |
hadoop fs -get /test/a.txt ./a2.txt
具体的 API 操作 Sample 可以看我的 github:
https://github.com/PengfeiMiao/bigdata-stu.git
1)客户端向 NameNode 请求上传xx文件;
2)NameNode 进行校验,响应是否可以上传文件;
3)请求上传第一个 Block (0-128M),请返回 DataNode;
4)返回 DataNode1,DataNode2,DataNode3 节点,表示采用三个节点存储数据 (首先考虑节点距离最近,再考虑负载均衡);
5)请求建立 Block 传输通道;
6)DataNode 应答成功。
7)传输数据 packet。
注:1个 packet (516Byte) 由 chunk (512Byte) 和 chunksum (4Byte) 构成;packet 最小为64K,即积累到64K才能传输。
节点距离计算
HDFS 写数据的时候,NameNode 会选择节点距离最近上传数据到 DataNode。
节点距离计算,指两个节点到达最近的共同祖先的距离总和。
机架感知
3个副本时,HDFS 的存储策略:
1)第一个副本在客户端所处的节点上随机选择一个,即本地机架,如果客户端在集群外,则随机选择一个;
2)第二个副本在另一个机架随机一个节点(保证可靠性);
3)第三个副本在第二个副本所在机架的随机节点(兼顾效率)。
1)客户端向 NameNode 请求下载xx文件;
2)NameNode 向客户端返回目标文件元数据;
3)选择节点距离最近的 DataNode 请求读数据,兼顾考虑负载能力;
4)DataNode 向客户端传输数据 (串行读取数据)。
fsimages 是真正存储的节点元数据信息,类似 Oracle 的 dbf 表空间文件。
edits 存储的是节点元数据的改变向量,类似 Oracle 的 redo 日志。
edits_inprogress_xxx 存储的是还没有更新到 edits 中的数据。
在没有 SecondaryNameNode 的集群中,NameNode 的工作流程:
- 系统启动后,将 edits 和 fsimage 从硬盘加载到内存中,方便数据的快速增删改。先加载 fsimage 内容到内存,然后在内存中按顺序把将edits中修改的内容执行一遍。
- 元数据发生变动时,edits 中记录改变向量,内存中数据也同步修改,但是不写入磁盘的 fsimage
- 系统关闭时,将 edits 中的改变向量按顺序执行,写入到 fsimage 中
有了 SecondaryNameNode 的集群,SecondaryNameNode 会定期询问 NameNode 是否到达检查点(checkpoint),如果到达了检查点,SecondaryNameNode 就辅助 NameNode 执行 edits 信息向 fsimage 中写入。
NameNode 工作流程
- 系统启动,将 edits 和 fsimage 从磁盘加载到内存中
- 客户端发出修改元数据的请求给 NameNode
- NameNode 将修改数据的改变向量写入磁盘的 edit_inprogress 中,然后同步修改掉内存中的数据
SecondaryNameNode 工作流程
- 向 NameNode 询问是否到达检查点(默认如果 edits 记录的修改次数达到100万,或者距离上个 checkpoint 时间间隔了1小时,就到达了检查点);
- 如果到达检查点,请求执行 Checkpoint;
- NameNode 的 edit_inprogress_001 中存储的改变向量滚动写入 edits 中。在 edit_inporgress_001 写入 edits 过程中,如果客户端向 NameNode 发出改变元数据的请求,这部分新的改变向量被暂时先写入 edit_inprogress_002 中;
- 将 NameNode 的 edits、fsimage 拷贝到 SecondaryNameNode;
- 将 edits、fsimage 信息加载到自己的内存中。在 fsimage 基础上顺序执行 edits 中的改变向量;
- 将内存中的计算结果写入磁盘 fsimage.checkpoint;
- 将 fsimage.checkpoint 拷贝给 NameNode;
- NameNode 将 fsimage.checkpoint 重命名为 fsimage,覆盖原有的 fsimage。
Q1:NameNode 中存储着很多的 edits 文件,有些已经在 fsimage 中合并过了。那么开机启动时,NameNode 如何判断还需要合并哪些 edits?
答:每个 edits 文件名上都有编号,例如 edits_0000000000000000423-0000000000000000424、edits_0000000000000000424-0000000000000000425,当这个 edits 被合并到 fsimage 后,生成的这个 fsimage 文件名上也会带有这个编号,例如 fsimage_0000000000000000425。系统开机启动时,向内存中加载完 fsimage_0000000000000000425,然后只会将编号大于 0000000000000000425 的 edits 文件加载到内存中进行执行。
Q2:NameNode 宕机时,为什么不能用 SecondaryNameNode 临时充当 NameNode?
答:因为 checkpoint 拷贝的时候,客户端如果向 DataNode 发出写元数据的请求,这部分元数据的修改被记录到了 NameNode 的 edit_inprogress 中,而 SecondaryNameNode 没有这个文件。所以如果 NameNode 宕机,使用 SecondaryNameNode 临时充当 NameNode 的话,会丢失掉这部分信息。
使用cat等命令无法直接查看 FSImage 和 Edits 文件,可以借助 hdfs oiv 查看 FSImage,借助 hdfs oev 查看 Edits 文件。
使用oiv命令查看FSImage文件:
hdfs oiv -p <文件类型> -d -o <转换后要输出的文件>
示例:
# 将fsimage_0000000000000000423转换成一个XML文件进行查看
hdfs oiv -p XML -i fsimage_0000000000000000423 -o /app/423.xml
通过输出的xml可以看到,FSImage中以树状结构存储了文件和所属文件夹之间的关联信息,以及文件的元数据。
但是FSImage中并没有存储每个数据块存储在哪台DataNode上。而是在系统上电启动时,DataNode主动向NameNode汇报自己服务器上存储的数据块信息。
使用oev命令查看Edits文件:
hdfs oev -p <文件类型> -i -o <转换后要输出的文件>
示例:
hdfs oev -p XML -i edits_0000000000000000424-0000000000000000425 -o /app/e424.xml
# 也可以查看当前正在写入的Edits文件
hdfs oev -p XML -i edits_inprogress_0000000000000000426 -o /app/inprogress.xml
一个数据块在 DataNode 上以文件形式存储在磁盘上,包括两个文件:
一个是数据本身,一个是元数据(包括数据块的长度、块数据的校验和,以及时间戳)。
1)DataNode 启动后向 NameNode 注册,通过后,周期性(默认6小时)的向 NameNode 上报所有的块信息。
2)心跳是每3秒一次,心跳返回结果带有 NameNode 给该 DataNode 的命令(如复制数据块到另一台机器,或删除某个数据块)。如果超过10分钟 + 10 * 3秒(超过10分钟,再给10次机会)还没有收到某个 DataNode 的心跳,就认为该节点不可用,认为该节点宕机了,不会再向该节点传输信息。
3)集群运行中,可以安全的加入或退出一些机器。
4)DN 向 NN 汇报当前节点信息的时间间隔,默认6小时;DN 扫描自己节点块信息列表的时间,默认也是6小时。
数据完整性
DataNode 保证数据完整性的做法
1. 当 DataNode 读取 Block 的时候,它会计算 CheckSum
2. 如果计算后的 CheckSum,与 Block 创建时值不一样,说明 Block 已经损坏。
3. Client 读取其他 DataNode 上的 Block
常见的校验算法有:crc(32)、md5(128)、sha1(160)。
DataNode 在其文件创建后周期性验证 CheckSum。
MapReduce:是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。
MapReduce 核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个 Hadoop 集群上。
优点:
- 易于编程:用户只关心业务逻辑,调用框架的接口即可
- 良好的扩展性:可以动态增加服务增加计算资源
- 高容错性:任何一台机器挂掉,可以将任务转移到其他节点
- 适合海量数据计算(TB/PB),几千台服务器共同计算
缺点:
- 不擅长实时计算。不能像 Mysql 一样处理毫秒级的计算
- 不擅长流式计算。可以用 Sparkstreamming、Flink
- 不擅长 DAG 有向无环图计算。可以用 Spark
“不擅长”不代表不能执行,只是没有其他专门的框架做的好
一个完整的MapReduce程序在分布式运行时有三类实例进程:
MRAppMaster 是 Yarn 的 ApplicationMaster 的子类,管理一个任务(job,也称MR)。
MapTask、ReduceTask 在进程列表中查不到,它们都属于进程列表中的 YarnChild 进程。
MapReduce 阶段组成:
一个 MapReduce 编程模型中,只能包含一个 Map 阶段和一个 Reduce 阶段,或者只有 Map 阶段;
不能有多个 Map 阶段、多个 Reduce 阶段的情景出现;
如果用户的业务逻辑非常复杂,就只能使用多个 MapReduce 程序串行执行。
Hadoop 在 Java 数据类型基础上又封装了新的数据类型,这些类都实现了 Hadoop 的序列化接口 Writable(位于 org.apache.hadoop.io 包下)
Java类型 | Hadoop Writable类型 |
---|---|
String | Text |
Boolean | BooleanWritable |
Byte | ByteWritable |
Int | IntWritable |
Float | FloatWritable |
Long | LongWtitable |
Double | DoubleWritable |
Map | MapWritable |
Array | ArrayWritable |
Null | NullWritable |
用户编写的程序分为三个部分:
1. Mapper
Mapper 代码编写:
以官方提供的WordCount示例程序中的Mapper为例
(位于 $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.2.3.jar 中的 WordCount):
// 继承Mapper类,泛型为<输入数据key的类型, 输入数据value的类型, 输出结果key的类型,输出结果value的类型>
// Text类型即为Hadoop封装的String类型
// IntWritable类型即为Hadoop封装的Int类型
// 输入数据中:key是偏移量,value是具体内容
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
// 实现map()方法
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
2. Reducer
Reducer 编写:
假如 Mapper 计算的结果 key-value 为以下内容:
{ a:12, b:10 }
{ a:1, c:1 }
{ a:2, b:3 }
到了 reduce() 方法中,就会将相同的 key 组成一个组,值为一个集合:
{
a: [12, 1, 2],
b: [10, 3],
c: [1]
}
以官方提供的WordCount实例程序中的Reducer为例:
// 需要继承Reducer类,泛型为:<输入数据key的类型, 输入数据value的类型, 输出结果key的类型,输出结果value的类型>
// Reducer输入数据的 key-value 即为 Mapper 的输出结果 key-value
public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
// 需要重写reduce方法
// key 即为相同key的组的key
// values 即为该key的value组的集合
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
3. Driver
相当于Yarn集群的客户端,用于提交我们整个程序到Yarn集群,提交的是封装了的MapReduce程序相关运行参数的job对象。
以官方提供的WordCount实例程序中的Reducer为例:
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length < 2) {
System.err.println("Usage: wordcount [...] " );
System.exit(2);
}
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
for (int i = 0; i < otherArgs.length - 1; ++i) {
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}
FileOutputFormat.setOutputPath(job,
new Path(otherArgs[otherArgs.length - 1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
Yarn 是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操作系统平台,而 MapReduce 等运算程序则相当于运行于操作系统之上的应用程序。
Yarn 是 Yet Another Resource Negotiator(另一种资源协调者)的缩写。
Yarn 是一个通用的资源管理系统和调度平台,可以为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处。
虽然 Yarn 属于 Hadoop 的一部分,但是 Yarn 不仅仅能运行 MapReduce,还可以运行 Tez、HBase、Spark、Flink 等等程序,理论上支持各种计算程序。Yarn不关心你做的什么,只负责管理资源(内存 和 CPU)。
- ResourceManager、NodeManager 是集群物理层面的组件,在 Hadoop 集群搭建过程中需要明确分配 Resourcemanager 是哪台服务器、NodeManager 是哪台服务器。
- ApplicationMaster 简称 AppMaster。在 MapReduce 程序中的 MRAppMaster 就是 AppMaster 的一个具体实现。属于 App 应用层面,Yarn 不关心该组件,而是由具体的应用程序(MapReduce、Spark等)去具体实现。
- ResourceManager、NodeManager、AppMaster 合称 Yarn 的三大组件。
- Container 容器:是硬件资源的抽象,多个程序之间就可以隔离运行。
核心流程:
1. 作业的提交:Client 提交作业给 ReourceManager
2. 资源的申请:AppMaster(MRAppMaster)向ResourceManager申请资源
3. 作业状态汇报:Container(MapTask、ReduceTask) 向 Container(MRAppMaster)汇报作业的执行状态
4. 节点状态汇报:NodeManager 向 ResourceManager 汇报节点的状态