MapReduce作业运行机制

MapReduce的整个过程如下图所示:
图1 Hadoop运行MapReduce作业的工作原理

<图1. Hadoop运行MapReduce作业的工作原理>

在最高层有5个独立实体:
1)客户端:提交MapReduce作业。
2)Yarn资源管理器:负责协调集群上计算机资源的分配。
3)Yarn节点管理器:负责启动和监视集群中机器上的计算容器(container)。
4)MapReduce的application master:负责协调运行MapReduce作业的任务。它和MapReduce任务在容器中运行,这些容器由资源管理器分配并由节点管理器进行管理。
5)分布式文件系统:一般为HDFS,用来与其他实体间共享作业文件。

下面详细讲解每一个步骤:

1、作业提交

Job的submit()方法创建一个内部的JobSummiter实例,并且调用其submitJobInternal()方法(图1步骤1)。提交作业后,waitForCompletion()每秒轮询作业的进度,如果发现自上次报告后有改变,便把进度报告到控制台。作业完成后,如果成功,就显示作业计数器;如果失败,则导致作业失败的错误被记录到控制台。

JobSummiter所实现的作业提交过程如下所述:

1)向资源管理器请求一个新应用ID,用于MapReduce作业ID。
2)检查作业的输出说明。例如,如果没有指定输出目录或输出目录已经存在,作业就不提交,错误抛回给MapReduce程序。
3)计算作业的输入分片。如果分片无法计算,比如因为输入路径不存在,作业就不提交,错误返回给MapReduce程序。
4)将运行作业所需要的资源(包括作业Jar文件、配置文件和计算所得的输入分片)复制到一个以作业ID命名的目录下的共享文件系统中(图1步骤3)。作业Jar的副本较多(由mapreduce.client.submit.file.replication属性控制,默认值为10),因此在运行作业的任务时,集群中有很多个副本可供节点管理器访问。
5)通过调用资源管理器的submitApplication()方法提交作业(图1步骤4)。

2、作业的初始化

资源管理器收到调用它的submitApplication()消息后,便将请求传递给Yarn调度器(scheduler)。调度器分配一个容器,然后资源管理器在节点管理器的管理下在容器中启动application master的进程(图1步骤5a和5b)。

MapReduce作业的application master是一个Java应用程序,它的主类是MRAppMaster。由于将接收来自任务的进度和完成报告(图1步骤6),因此application master对作业的初始化是通过创建多个簿记对象以保持对作业进度的跟踪来完成的。接下来,它接收来自共享文件系统的、在客户端计算的输入分片(图1步骤7)。然后对每一个分片创建一个map任务对象以及由mapreduce.job.reduces属性(通过作业的setNumReduceTasks()方法设置)确定的多个reduce对象。任务ID在此时分配。

application master必须决定如何运行构成MapReduce作业的各个任务。如果作业很小,就选择和自己在同一个JVM上运行任务。与在一个节点上顺序运行这些任务相比,当application master判断在新的容器中分配和运行任务的开销大于并行运行他们的开销时,就会发生这一情况。这样的作业称为uberized,或者作为uber任务运行。

哪些作业是小作业?默认情况下,小作业就是少于10个mapper且只有1个reducer且输入大小小于一个HDFS块的作业(通过设置mapreduce.job.ubertask.maxmaps、mapreduce.job.ubertask.maxreduces和mapreduce.job.ubertask.maxbytes可以改变这几个值)。必须明确启用Uber任务(对于单个作业,或者是对整个集群),具体方法是将mapreduce.job.ubertask.enable设置为true。

(关于uber模式更详细的介绍,可查看:MapReduce的Uber运行模式

最后,在任何任务运行之前,application master调用setupJob()方法设置OutputCommitter。FileOutputCommitter为默认值,表示将建立作业的最终输出目录及任务输出的临时工作空间。

3、任务的分配

如果作业不适合作为uber任务运行,那么application master就会为该作业中的所有map任务和reduce任务向资源管理器请求容器(图1步骤8)。首先为Map任务发出请求,该请求优先级要高于reduce任务的请求,这是因为所有的map任务必须在reduce的排序阶段能够启动前完成。直到有5%的map任务已经完成时,为reduce任务的请求才会发出。

reduce任务能够在集群中任意位置运行,但是map任务的请求有着数据本地化局限,这也是调度器所关注的。在理想情况下,任务是数据本地化(data local)的,意味着任务在分片驻留的同一节点上运行。可选的情况是,任务可能是机架本地化(rack local)的,即和分片在同一机架而非同一节点上运行。有一些任务既不是数据本地化,也不是机架本地化,它们会从别的机架,而不是运行所在的机架上获取自己的数据。对于一个特定的作业运行,可以通过查看作业的计数器来确定在每个本地化层次上运行的任务的数量。

请求也为任务指定了内存需求和CPU数。在默认情况下,每个map任务和reduce任务都分配到1024MB的内存和一个虚拟的内核,这些值可以在每个作业的基础上进行配置,分别通过4个属性来设置:

选项默认值描述
mapreduce.map.memory.mb1024每个Map Task需要的内存量
mapreduce.map.cpu.vcores1每个Map Task需要的虚拟CPU个数
mapreduce.reduce.memory.mb1024每个Reduce Task需要的内存量
mapreduce.reduce.cpu.vcores1每个Reduce Task需要的虚拟CPU个数

4、任务的执行

一旦资源管理器的调度器为任务分配了一个特定节点上的容器,application master就通过与节点管理器通信来启动容器(图1步骤9a和9b)。该任务由主类为YarnChild的一个Java应用程序执行。在它运行任务之前,首先将任务需要的资源本地化,包括作业的配置,Jar文件和所有来自分布式缓存的文件(图1步骤10)。最后,运行map任务或reduce任务(图1步骤11)。

YarnChild在指定的JVM中运行,因此用户定义的map或reduce函数(甚至是YarnChild)中的任何缺陷不会影响到节点管理器,例如导致其崩溃或挂起。

每个任务都能够执行搭建(setup)和提交(commit)动作,他们和任务本身在同一个JVM执行,并由作业的OutputCommitter确定。对于基于文件的作业,提交动作将任务输出由临时位置搬移到最终位置。提交协议确保当推测(speculative execution)被启用时,只有一个任务副本被提交,其他的都被取消。

Streaming

Streaming运行特殊的map任务和reduce任务,目的是运行用户提供的可执行程序,并与之通信。如下图:
在这里插入图片描述
Streaming任务使用标准输入和输出流与进程(可以用任何语言编写)进行通信。在任务执行过程中,Java进程都会把“输入键-值对”传给外部的进程,后者通过用户定义的map函数和reduce函数来执行它并把“输入键-值对”传回Java进程。从节点管理器的角度看,就像其子进程自己在运行map或reduce代码一样。

5、进度和状态的更新

MapReduce作业是长时间运行的批量作业,运行时间范围从数秒到数小时。这可能是一个很长的时间段,所以对于用户而言,能够得知关于作业进展的一些反馈是很重要的。一个作业和它的每个任务都有一个状态(status),包括:作业或任务的状态(比如:运行中、成功完成、失败)、map和reduce的进度、作业计数器的值、状态消息或描述(可以由用户代码来设置)。这些状态信息在作业期间不断改变,它们是如何与客户端通信的呢?

任务在运行时,对其进度(progress,即任务完成百分比)保持追踪。对map任务,任务进度是已处理输入所占的比例。对reduce任务,情况稍微有点复杂,但系统仍然会估计已处理reduce输入的比例。整个过程分成三个部分,与shuffle的三个阶段相对应。比如,如果任务已经执行reducer一半的输入,那么任务进度便是5/6,这是因为已经完成复制和排序阶段(每个占1/3),并且已经完成reduce阶段的一半(1/6)。

MapReduce中进度的组成

进度并不总是可测量的,但是虽然如此,它能告诉hadoop有个任务正在做一些事情。比如,正在写输出记录的任务是有进度的,即使此时这个进度不能用需要写的总量的百分比来表示(因为即便是产生这些输出的任务,也可能不知道需要写的总量)。

进度报告很重要。构成进度的所有操作如下:
1)读入一条输入记录(在mapper或reducer中);
2)写入一条输出记录(在mapper或reducer中);
3)设置状态描述(通过Reporter或TaskAttemptContext的setStatus()方法);
4)增加计数器的值(使用Reporter的incrCounter ()方法或Counter的increment()方法);
5)调用Reporter或TaskAttemptContext的progress()方法。

任务也有一组计数器,负责对任务运行过程中各个事件进行计数,这些计数器要么内置于框架中,例如已写入的map输出记录数,要么由用户自己定义。

当map任务或reduce任务运行时,子进程和自己的父application master通过umbilical接口通信。每隔3秒钟,任务通过这个umbilical接口向自己的application master报告进度和状态(包括计数器),application master会形成一个作业的汇聚视图(aggregate view)。

资源管理器的界面显示了所有运行中的应用程序,并且分别有链接指向这些应用各自的application master的界面,这些界面展示了MapReduce作业的更多细节,包括其进度。

在作业期间,客户端每秒钟轮询一次application master以接收最新状态(轮询间隔通过mapreduce.client.progressmonitor.pollinterval设置)。客户端也可以使用Job的getStatus()方法得到一个JobStatus的实例,后者包含作业的所有状态信息。

上述过程图解如下:
在这里插入图片描述
6、作业的完成

当application master收到作业最后一个任务已完成的通知后,便把作业的状态设置为“成功”。然后,在Job轮询状态时,便知道任务已成功完成,于是Job打印一条消息告知用户,然后从waitForCompletion()方法返回。Job的统计信息和计数值也在这个时候输出到控制台。

如果application master有相应的设置,也会发送一个HTTP作业通知。希望收到回调指令的客户端可以通过mapreduce.job.end-notification.url属性来进行这项设置。

最后,作业完成时,application master 和任务容器清理其工作状态(这样中间输出将被删除),OutputCommitter的commitJob()方法会被调用。作业信息由作业历史服务器存档,以便日后用户需要时可以查询。

完毕。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值