【Hadoop】20-剖析MapReduce作业运行机制

可以通过一个简单的方法调用来运行MapReduce作业:Job对象的submit()方法。注意,也可以调用waitForCompletion(),它用于提交以前没有提交过的作业,并等待它的完成。submit()方法调用封装了大量的处理细节。本小节将揭示Hadoop运行作业时所采取的措施。

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

1.作业的提交

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


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

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

2.作业的初始化

资源管理器收到调用它的submitApplication()消息后,便将清求传递给YARN调度器(scheduler)。调度器分配一个容器,然后资源管理器在节点管理器的管理下在容器中启动application master的进程(步骤5a和5b)。
MapReduce作业的application master是一个Java应用程序,它的主类是MRAppMaster。由于将接受来自任务的进度和完成报告(步骤6),因此application master对作业的初始化是通过创建多个簿记对象以保持对作业进度的跟踪来完成的。接下来,它接受来自共享文件系统的、在客户端计算的输人分片(步骤7)。然后对每一个分片创建一个map任务对象以及由mapreduce.job.reduces属性(通过作业的setNumReduceTasks()方法设置)确定的多个reduce任务对象。任务ID在此时分配。

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

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

最后,在任何任务运行之前,application master调用setupJob()方法设置0utputCmmitter。File0utputCmmitter为默认值,表示将建立作业的最终输出目录及任务输出的临时工作空间。提交协议(commit protocol)将在后面的《任务的执行》章节介绍。

3.任务的分配

如果作业不适合作为uber任务运行,那么applicationmaster就会为该作业中的所有map任务和reduce任务向资源管理器请求容器(步骤8)。首先为Map任务发出请求,该请求优先级要高于reduce任务的请求,这是因为所有的map任务须在reduce的排序阶段能够启动前完成(详见shulfle节)。直到有5%的map任务已经完成时,为reduce任务的请求才会发出(详见Hadoop的其他属性一节)。
reduce任务能够在集群中任意位置运行,但是map任务的请求有着数据本地化局限,这也是调度器所关注的(详见资源请求一节)。在理想的情况下,任务是数据本地化(data local)的,意味着任务在分片驻留的同一节点上运行。可选的情况是,任务可能是机架本地化(rack local)的,即和分片在同一机架而非同一节点上运行。有一些任务既不是数据本地化,也不是机架本地化,它们会从别的机架,而不是运行所在的机架上获取自己的数据。对于一个特定的作业运行,可以通过查看作业的计数器来确定在每个本地化层次上运行的任务的数量参见表9-6)。
请求也为任务指定了内存需求和CPU数。在默认情况下,每个map任务和reduce任务都分配到1024MB的内存和一个虚拟的内核,这些值可以在每个作业的基础上进行配置(遵从于10.3.3节描述的最大值和最小值),分别通过4个属性来设置:

  • mapreduce.map.memory.mb、
  • mapreduce.reduce.memory.mb、
  • mapreduce.map.cpu.vcores和
  • mapreduce.reduce.cpu.vcoresp.memory.mb。

4.任务的执行

一旦资源管理器的调度器为任务分配了一个特定节点上的容器,application master就通过与节点管理器通信来启动容器(步骤9a和9b)。该任务由主类为YarnChild的一个Java应用程序执行。在它运行任务之前,首先将任务需要的资源本地化,包括作业的配置、JAR文件和所有来自分布式缓存的文件(步骤10,参见9,4,2节)。最后,运行map任务或reduce任务(步骤11)。
YarnChild在指定的JVM中运行,因此用户定义的map或reduce函数(甚至是YarnChild)中的任何缺陷不会影响到节点管理器,例如导致其崩溃或挂起。
每个任务都能够执行搭建(setup)和提交(commit)动作,它们和任务本身在同一个JVM中运行,并由作业的OutputCommitter确定。对于基于文件的作业,提交动作将任务输出由临时位置搬移到最终位置。提交协议确保当推测执行(speculative execution)被启用时(参见7.4.2节推测执行),只有一个任务副本被提交,其他的都被取消。
Streaming

Streaming运行特殊的map任务和reduce任务,目的是运行用户提供的可执行程序,并与之通信(参见下图)。

Streaming任务使用标准输人和输出流与进程(可以用任何语言写)进行通信。在任务执行过程中,Java进程都会把输人键.值对传给外部的进程,后者通过用户定义的map函数和reduce函数来执行它并把输出键·值对传回Java进程。从节点管理器的角度看,就像其子进程自己在运行map或reduce代码一样。

5.进度和状态的更新

MapReduce作业是长时间运行的批量作业,运行时间范围从数秒到数小时。这可能是一个很长的时间段,所以对于用户而言,能够得知关于作业进展的一些反馈是很重要的。一个作业和它的每个任务都有一个状态(status),包括:作业或任务的状态(比如,运行中,成功完成,失败)、map和reduce的进度、作业计数器的值、状态消息或描述(可以由用户代码来设置)。这些状态信息在作业期间不断改变,它们是如何与客户端通信的呢?
任务在运行时,对其进度(progress,即任务完成百分比)保持追踪。对map任务,任务进度是已处理输人所占的比例?对reduce任务,情况稍微有点复杂,但系统仍然会估计已处理reduce输人的比例。整个过程分成三部分,与shuffle的三个阶

段相对应(详情参见7.3节shuffle和排序)。比如,如果任务已经执行reducer一半的输人,那么任务的进度便是5/6,这是因为已经完成复制和排序阶段(每个占1/3),并且已经完成reduce阶段的一半1/6)。

MapReduce中进度的组成
进度并不总是可测量的,但是虽然如此,它能告诉Hadoop有个任务正在做一些事情·比如,正在写输出记录的任务是有进度的,即使此时这个进度不能用需要写的总量的百分比来表示(因为即便是产生这些输出的任务,也可能不知道需要写的总量)。
进度报告很重要·构成进度的所有操作如下:

  • 读入一条输入记录(在mapper或reducer中)
  • 写入一条输出记录(在mapper或reducer中)
  • 设置状态描述(通过Reporter或TaskAttemptContext的setStatus()方法)
  • 增加计数器的值(使用Reporter的incrCounter()方法或Counter的increment()方法)
  • 调用Reporter或TaskAttemptContext的progress()方法
任务也有一组计数器,负责对任务运行过程中各个事件进行计数(详情参见2.3.2节Java MapReduce),这些计数器要么内置于框架中,例如已写人的map输出记录数,要么由用户自己定义。
当map任务或reduce任务运行时,子进程和自己的父application master通过umbilical接口通信。每隔3秒钟,任务通过这个umbilical接口向自己的application master报告进度和状态(包括计数器),application master会形成一个作业的汇聚视图(aggregateview)。
资源管理器的界面显示了所有运行中的应用程序,并且分别有链接指向这些应用各自的application master的界面,这些界面展示了MapReduce作业的更多细节,包括其进度。

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

对上述过程的图解:


6.作业的完成

当application master收到作业最后一个任务已完成的通知后,便把作业的状态设置为“成功”。然后,在〕ob轮询状态时,便知道任务已成功完成,于是Job打印一条消息告知用户,然后从waitForCompletion()方法返回。Job的统计信息和计数值也在这个时候输出到控制台。如果applicationmaster有相应的设置,也会发送一个HTTP作业通知。希望收到回调指令的客户端可以通过mapreduce.job.end-notification.url属性来进行这项设置。
最后,作业完成时,application master和任务容器清理其工作状态(这样中间输出将被删除),0utputCommitter的commitJob()方法会被调用。作业信息由作业历史服务器存档,以便日后用户需要时可以查询。

猜你喜欢

转载自blog.csdn.net/shenchaohao12321/article/details/80378675
今日推荐