@author Jinxin Li
深入理解Hadoop生态下MapReduce框架的Map步骤
在阅读本文之前需要具有一定深度的MapReduce框架知识
本文讲解为了方便在本地模式下运行
Map阶段的范围
在MapReduce的工作流程中,Map真正的范围是根据理解重新划分的.
广义的Map阶段是从客户端开始提交job开始时,到MapTask全部归并排序完成
狭义可以理解为从MapReduce ApplicationMaster给计算提供容器开始,到归并排序完容器使用完毕为结束.
Map阶段的步骤
一. 客户端
1.确定待处理文本
如E:/input/word.txt.确定处理目标为统计word.txt里的单词数量
2.客户端切片
客户端获取处理数据的信息,然后根据配置文件,形成一个任务的逻辑规划
(Configuration)配置文件:
core-default.xml, core-site.xml, mapred-default.xml, mapred-site.xml, yarn-default.xml, yarn-site.xml, hdfs-default.xml, hdfs-site.xml
(Split)数据切片:
仅仅是客户端在逻辑上对输入进行分片
并不会在磁盘上将其切分成片进行存储
数据切片是MapReduce程序计算输入数据的单位
一个切片会对应启动一个MapTask。
默认切片大小等于HDFS默认块大小(128M)
切片不考虑数据集整体,对逐一文件进行切片切片方法:TextInputFormat调用父类的FileInputFormat的getSplit()方法
3.客户端向服务器提交(job submit)
生成临时文件,然后将文件上传到服务器
上传的文件:
切片规划文件InputSplit:记录了切片的元数据信息,比如起始位置,长度以及所在的节点列表等
配置信息job.xml:基础配置信息与你设置的信息如设定的使用类,驱动信息,文件路径
使用的jar包 wordcount.jar
二. Map阶段
1. Yarn分配容器,启动MapTask
启动MapTask1,MapTask2…
基础概念回顾------------------------------------------------------------------
Yarn:主要负责Hadoop集群中的资源调度.
ResourceManager:它是Yarn中的老大,权限最高,所有针对资源分配的指令都有它发出,当有一个Job任务要执行的时候,必然会先找ResourceManager,然后它会为此Job任务开启一个 ApplicationMaster
NodeManager:NodeManager代表集群中的每一台机器上的资源管理者,它的本职工作就是向老大ResourceManager汇报每一台机器的资源使用情况,并且根据ResourceManager下发的指令来具体对本机的资源进行分配使用.
ApplicationMaster:对应每一个Job任务,负责为Job向ResourceManager申请资源,申请资源以后,负责告诉NodeManager去运行对应的Job任务,在运行的过程中也要时刻负责监控任务的执行情况.
Container:它就是一个针对每一台机器的资源的抽象封装,在整个体系中用来表达我们的资源(内存,CPU,网络,磁盘)
具体流程---------------------------------------------------------------------------------
客户端提交Job到ResourceManager
ResourceManager 为当前Job启动ApplicationMaster,由于ApplicationMaster运行也是需要资源的,所以ApplicationMaster启动后,就会有一个Container,Container所封装的资源就是为ApplicationMaster提供的。再者由于资源具体都是存在于每一台机器上 固然当前ApplicationMaster是运行在集群中的一个NodeManager上。
接下来,ApplicationMaster会根据当前Job的情况向ResourceManager申请资源来运行Job的具体Task,当前Job有上个Task,固然要申请3份资源,也就有三个封装资源的对象Container。
所有的资源分配都是由ResourceManager下达指令给NodeManager分配的。
ApplicationMaster为对应的Task申请完资源后,会告诉NodeManager去运行每一个Task
每个Task可能运行在不同的节点机器上,也可能多个Task都运行在一台节点机器上,这个不固定,得看当时集群的资源情况。当Job的内阁Task开始运行,ApplicationMaster要负责监控整个Job的运行状态和容错等相关事宜
当Job的每一个MapTask执行成功后,也意味着整个Job执行完毕,此时ApplicationMaster会申请注销自己,所有为当前封装资源都得到释放。
2. 读取文本
MapTask使用InputFormat接口的实现类处理不同的文本
读取文本转换成KV值
使用默认的InputFormat的中的RecordReader()方法实现,由子类TextInputFormat实现
K V
的读取(其中K值为行际偏移量 V值为一行的数据,不包含换行信息) 然后将K-V值传入继承了Mapper父类的map(K,V)
然后使用context.write(K,V)进行序列化,对处理后的数据进行向缓冲区写入
3. 写入圆形缓冲区
圆形缓冲区本质是一个二维数组,内存存储着不断写入的序列化的字节信息
圆形缓冲区分两部分,一部分是存储索引信息Meta, 一部分是数据信息Records
圆形缓冲区内的排序是分区排序的
圆形缓冲区两端进行,默认总大小为100 M,两端进行,为了防止数据形成重叠,存储容量在80%时进行反向存入
在反向存入之前要对字节数组进行拷贝,然后拿出来,称为数据溢出
拷贝完之后对数据进行排序分区
排序选择方法为快速排序
排序过程中仅仅是使用两个V值对象,调用in方法设定对象,所以可以一直用一个对象
@Override public void readFields(DataInput in) throws IOException { upFlow = in.readLong(); downFlow = in.readLong(); sumFlow = in.readLong(); }
在进行比较时,比较的两个会发生反序列化,进行比较后,调整索引的索引值,而不动数据值
在溢出过程及合并的过程中,都要调用Partitioner进行分区和针对key进行排序
4. 归并排序
对环形缓冲区溢出全部字节数组后进行统一归并排序
归并排序的过程中也会使用固定的两个对象,分别进行解序列化比较,然后将最低的字节序列以此写入到归并后的新数列
5. 整合所有MapTask产生的归并排序序的数组
进行按区分开写入到Reduce Task磁盘中
6. 所有的MapTask任务完成
启动ReduceTask,并告知ReduceTask的处理范围(数据分区)
ReduceTask根据自己的分区号,去各个MapTask机器上取相应的结果分区数据
三. Reduce阶段
Reduce详细过程图
1. Copy阶段
ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
2. Merge阶段
在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
3. Sort阶段
按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
4. Reduce阶段
reduce()函数将计算结果写到HDFS上。本地模式写在本地
四. Shuffle阶段
Shuffle是对Map后半部分与Reduce的前半部分的总结