工作这段时间已经写过不少 MapReduce Job,但未曾系统地研究过它的运行流程,接下来要花一点时间整理一下。先从概述整个流程开始,随后结合源码简单分析一下各个关键组件。
董西成的《Hadoop 技术内幕:深入解析 MapReduce 架构设计与实现原理》一书中讲到:
回调机制是一种常见的设计模式。它将工作流内的某个功能按照约定的接口暴露给外部使用者,为外部使用者提供数据,或要求外部使用者提供数据。
Hadoop MapReduce 对外提供的 5 个组件(InputFormat、Mapper、Partitioner、Reducer 和 OutputFormat)实际上全部属于回调接口。当用户按照约定实现这几个接口后,MapReduce 运行时环境会自动调用它们。
图 1 来自《Hadoop 权威指南(第 4 版)》第 7 章,展示了一个 MapReduce Job 的通常流程,简单来说就是:输入文件经 InputFormat 切分成多个 input split,每个 input split 交由一个 Mapper 处理,每个 Mapper 的输出都会先缓存在内存中,缓冲区容量达到一定阈值后会“溢写”(spill)到本地磁盘。注意,Mapper 的输出记录在写入缓冲区之前就会调用 Partitioner 计算出其 partition,而“溢写”之前会按照 partition 和 key 对输出记录进行排序。如果设置了 Combiner,有可能(需要满足一定条件)在排序后的结果上应用一次。这样,每个 Mapper 会“溢写”出多个文件,最后会合并成一个有序的大文件,合并之后还是可能会应用一次 Combiner。Reducer 端会监测每个 Mapper 是否完成,待其完成后从 Mapper 端拉取(fetch)所有属于它的 partition,然后进行多次归并得到整体按 key 排序的结果,输入给 Reducer,Reducer 处理之后调用 OutputFormat 将结果写入 HDFS,至此整个 MapReduce 流程就结束了。
现在的 MapReduce Job 一般运行在 YARN 之上,有必要了解一下 YARN 的各个组件是如何协调配合完成 Job 运行的。在此之前,先了解一下 YARN 的架构,如 Hadoop 官方文档提供的图 2 所示,YARN 主要有以下几类核心组件:
- Resource Manager(RM):负责整个集群的资源管理和分配,包括处理客户端请求、启动和监控 AM、监控 NM、资源的分配和调度。
- Application Master(AM):RM 会为每个 Job 都启动一个 AM,它负责从 RM 协调计算资源,与 NM 配合执行和监控该 Job 的所有 Task。
- Node Manager(NM):每个节点上资源和任务的管理器,负责当前节点任务的运行、资源的管理和监控,并向 RM 报告,同时需要执行 RM 和 AM 的命令。
- Container:节点上计算资源的抽象,可以看作一个“苦工”(AM 可以看作“包工头”,NM 可以看作“小队长”),负责运行具体的 Task。
好了,下面继续讲 MapReduce,图 3 同样来自《Hadoop 权威指南(第 4 版)》第 7 章,展示了 MapReduce Job 在 YARN 上面的运行过程:
- Job 提交:调用 Job 对象的
submit()
方法开始提交,创建JobSubmitter
实例并调用其submitJobInternal()
方法(step 1),向 RM 申请一个 applicaiton ID(step 2),确认输出路径、计算 input split、将配置、Jar 包、计算出的 input split 元信息放到 HDFS 指定路径下(step 3),最后调用submitApplication()
方法提交 Job(step 4)。 - Job 初始化:RM 收到 Job 提交请求后分配一个 container(step 5a), 然后在其中启动一个主类名为
MRAppMaster
的 AM 进程(step 5b),AM 初始化 Job 并为监控 Task 的运行做准备(step 6),并从 HDFS 指定路径获取 input split 元信息(step 7)。 - Task 分配:AM 向 RM 申请运行 Map Task 的 container,等 Map Task 进度到达一定阈值后再申请运行 Reduce Task 的 container(step 8),注意:RM 需要为 Map Task 分配尽量靠近输入数据的 container,而 Reduce Task 没有这样的需求(Reducer 需要从所有 Mapper 拉取输出)。
- Task 运行:如果一个 Task 已经被分配了 container,AM 向 NM 发送命令(step 9a),由 NM 启动相应的 contaier(step 9b),然后在 container 中启动一个主类名为 YarnChild 的进程,从 HDFS 拉取配置、Jar 包等资源(step 10),最后开始运行 map 或 reduce Task(step 11)。
有关 MapReduce 流程的简要介绍就到这里,后面开始结合源码分析它的 5 个核心组件:InputFormat、Mapper、Partitioner、Reducer 和 OutputFormat。