10、MapReduce的工作原理

来源:互联网 发布:selenium python ie 编辑:程序博客网 时间:2024/05/21 16:22

1、运行MapReduce作业


JobClient的runJob()方法是用于新建JobClient实例和调用其submitJob()方法的简便方法(步骤1),submitJob()方法实现的作业提交过程如下:

1)向jotracker请求一个新的作业ID(通过JobTracker的getNewJodId())(步骤2)。

2)检查作业的输出说明。比如如果没有制定输出目录或者已经存在,作业就不会提交,并有错误返回给MapReduce程序。

3)计算作业的输入划分。如果划分无法计算,比如输入目录不存在,作业就不会提交,并有错误返回给MapReduce程序。

4)将运行作业所需要的资源——包括JAR文件、配置文件和计算得到的输入划分,复制到一个以作业ID命名的目录中jobtracker的文件系统。(步骤3

5)告诉jobtracker作业准备执行(通过JobTracker的submitJob()方法)。(步骤4

JobTracker接收到对其submitJob()方法的调用之后,会把此调用放入内部队列中,交由作业调度器进行调度,并进行初始化。初始化包括该正在运行的作业的对象,它封装任务和记录信息,以便跟踪任务状态和进程。(步骤5

要创建运行任务列表,作业调度器首先从共享文件系统中获取JobClient已计算的输入划分信息。(步骤6

TaskTracker执行一个简单的循环,定期f发送心跳调用JobTracker。心跳方法告诉jobTracker是否taskTracker活着,同时也是二者之间的消息通道。taskTracker会指明是否已经准备运行新的任务,jobTracker也会为它分配任务。(步骤7

现在taskTracker已经被分配任务了,接下来就是运行任务。首先,本地化作业的JAR文件,将它从共享文件系统中复制到taskTracker所在的文件系统中,同时将程序所需要的全部文件从分布式缓存复制到本地磁盘。(步骤8)然后为任务新建一个本地工作目录,并将JAR文件内容解压到这个文件夹下。第三步,新建一个taskRunner实例运行任务。taskRunner启动一个新的java虚拟机(步骤9)来运行每个任务(步骤10)。

2、进度和状态的更新、作业完成

状态更新:一个作业和每个任务都有一个状态,包括:作业和任务的状态(运行、完成或失败)、map和reduce的进度、作业计数器的值、状态描述和消息(可以由用户代码设置)。如果任务报告了进度,便会设置一个标志来表示状态变化将会送到tasktracker。在另一个线程中每个三秒检查此标志,如果已设置就告诉tasktracker当前的状态。同时,tasktracker每隔五秒发送心跳到jobtracker(5秒时最小的时间间隔,心跳间隔由集群的大小决定),并在此次调用中(心跳调用)所有由tasktracker进行的任务的状态都会被发送至jobtracker。jobtracker将这些更新合并起来产生一个全局视图,表明现在所有运行的作业以及其所含任务的状态。客户端也可以使用JobClient的getJob()得到一个RunningJob的实例,后者含有作业的所有状态。

作业完成:jobtracker收到作恶最后一个任务已完成的通知后,便把作业的状态设置成“已完成”。如果jobtracker由相应的设置,也会发送一个HTTP作业通知。希望收到回调的客户端可以通过job.end.notification,url设置。最后,jobtracker清空作业的工作状态,指示tasktracker也清空作业的工作状态(如删除中间输出)。

3、失败

任务失败:子任务失败的情况,常见的是map和reduce的用户代码抛出异常。此时,子任务JVM进程会在退出之前向tasktracker父进程发送错误报告。tasktracker将此次任务尝试标志为failed,释放一个槽以便运行另一个任务。另一种一场是子JVM突然退出,可能由JVM的错误,MapReduce用户代码某些特殊原因造成的,tasktracker会注意到进程已退出,并将此次任务标志为failed。对于任务挂起,处理方式则不同。tasktracker注意到有一段时间没有收到进程更新,因此将任务标志为failed,在此之后,子JVM进程将被自动杀死。

tasktracker失败:如果tasktracker由于崩溃或运行缓慢而失败,它将停止向jobtracker发送心跳。jobtracker会注意到此tasktracker已经停止发送心跳,将它从等待任务调度的tasktracker池中移除。tasktracker还可以被加入黑名单,即使它没有失败。如果它上面的任务失败次数远高于集群的平均失败次数,会被加入黑名单。

jobtracker失败:jobtracker失败是最为严重的失败。目前版本中已经通过运行多个jobtracker来解决这个问题,任何时候都只有其中一个主jobtracker(使用zookeeper作为jobtracker的一种协调机制决定哪一个是主jobtracker)

4、shuffle和排序

MapReduce保证每个reducer的输入都已经按键排序,系统执行排序的过程——map输出到reducer作为后者的输入——即称为shuffle(混洗或洗牌)。


map端:map函数开始产生输出时,并不是简单的将它写到磁盘。它利用缓存的方式先写到内存,并处于效率的原因预先进行排序,如图所示。每个map任务都有一个环形缓冲区,任务会把输出写到此。默认情况下,缓冲区大小100MB(io.sort.mb属性调整),当缓冲区达到指定大小的80%(io.sort.spill.percent,默认0.80,或者80%),一个后台进程开始把内容溢写(spill)到磁盘中。于此同时,任务继续向缓冲区写入数据,如果在此期间缓冲被填满,map会阻塞直到溢写过程结束。在写到磁盘之前,线程会根据数据最终被传送的reducer,将数据划分成相应的分区。在每个分区中,后台线程按键进行内排序(in-memory sort)。此时如果还有一个combiner,它将基于排序后输出运行。一旦缓冲区达到溢写阈值,就会新建一个溢写文件,因此map任务写入最后一个输出记录后,会有若干个溢写文件,溢写文件会被合并成一个已分区且已排序的输出文件。reducer通过HTTP方式得到输出文件的分区。用于文件分区的工作线程的数量由任务的tracker.http.threads属性控制,此设置针对每个tasktracker,而不是针对每个map任务槽。默认值是40,在运行大型作业的大型集群上,此值可以根据需要调整。

reduce端:map任务可以在不同时间完成,因此只要一个任务结束,reduce任务开始复制其输出。reduce由少量的复制线程,默认值是5个线程,可以通过设置mapred.reduce.parallel.copies属性改变。如果map任务输出相当小,则被复制到reduce tasktracker的内存中(缓冲区大小由mapred.job.shuffle.input.buffer.percent属性控制),否则,map输出被复制到磁盘。一旦内存缓冲区达到阀值大小(由mapred.job.shuffle.merge.percent决定)或达到map输出阀值(mapred.inmem.merge.threshold控制),则合并后溢出写到磁盘中。随着磁盘上积累的副本增多,后台线程会将他们合并成一个更大、排好序的文件。所有map输出被复制期间,reduce任务进入排序阶段(更恰当的说应该是合并阶段,因为排序已经在map端完成),这个阶段合并map的输出,并将维持顺序排序。最后阶段,即reduce阶段,合并直接把数据输入reduce函数。此阶段的输出写到输出文件系统,一般为HDFS。

0 0
原创粉丝点击