在Hadoop中提交一个任务大致分为6个步骤,分别为:
作业提交 -> 作业初始化 –> 任务分配 –> 任务执行 –> 更新任务执行进度和状态 -> 作业完成
目录
客户端(Client):编写MapReduce程序,配置作业,提交作业
JobTracker:协调作业的运行,分配作业,初始化作业,与TaskTracker进行通信
TaskTracker:负责运行作业,保持与JobTracker的通信,
HDFS:保持作业的数据与结构
作业提交
JobClient使用runJob方法新建一个JobClient 实例,然后调用submitJob()方法进行作业的提交,提交作业的具体过程如下:
- 通过调用JobTracker对象的getNewJobId()方法从JobTracker处获得一个作业ID。
- 检查作业的相关路径。如果输出路径存在,作业将不会被提交(保护上一个作业运行结果)。
- 计算作业的输入分片,如果无法计算,例如输入路径不存在,作业将不被提交,错误返回给 MapReduce 程序。
- 将运行作业所需资源(作业 Jar 文件、配置文件和计算所得到的分片)复制到HDFS 上。JAR文件默认会有10个副本(mapred.submit.replication属性控制);输入划分信息告诉了JobTracker应该为这个作业启动多少个map任务等信息。
- 告知 JobTracker 作业准备执行(使用 JobTracker 对象的 submitJob()方法来真正提交作业)。
作业初始化
当 JobTracker 收到 Job 提交的请求后,将 job 保存在一个内部队列,并让 Job Scheduler(作业调度器)处理并初始化。
初始化涉及到创建一个封装了其 tasks 的 job 对象,并保持对 task 的状态和进度的根据(step 5)。当创建要运行的一系列 task 对象后,Job Scheduler 首先开始从文件系统中获取由 JobClient 计算的 input splits(step 6),然后再为每个 split 创建 map task。
任务的分配
TaskTracker 和 JobTracker 之间的通信和任务的分配是通过心跳机制完成的。TaskTracker 作为一个单独的 JVM,它执行一个简单的循环,主要实现每隔一段时间向 JobTracker 发送心跳,告诉 JobTracker 此 TaskTracker 是否存活,是否准备执行新的任务。如果有待分配的任务,他就会为 TaskTracker 分配一个任务。
初始化完毕后,作业调度器会获取输入分片信息(input split),每个分片创建一个map任务。
接下来就是任务分配了,这个时候tasktracker会运行一个简单的循环机制定期发送心跳给jobtracker,心跳间隔是5秒,程序员可以配置这个时间,心跳就是jobtracker和tasktracker沟通的桥梁,通过心跳,jobtracker可以监控tasktracker是否存活,也可以获取tasktracker处理的状态和问题,同时tasktracker也可以通过心跳里的返回值获取jobtracker给它的操作指令。
任务分配好后就是执行任务了。在任务执行时候jobtracker可以通过心跳机制监控tasktracker的状态和进度,同时也能计算出整个job的状态和进度,而tasktracker也可以本地监控自己的状态和进度。当jobtracker获得了最后一个完成指定任务的tasktracker操作成功的通知时候,jobtracker会把整个job状态置为成功,然后当客户端查询job运行状态时候(注意:这个是异步操作),客户端会查到job完成的通知的。如果job中途失败,mapreduce也会有相应机制处理,一般而言如果不是程序员程序本身有bug,mapreduce错误处理机制都能保证提交的job能正常完成。
JobTracker接收到作业后,将其放在一个作业队列里,等待作业调度器对其进行调度(这里是不是很像微机中的进程调度呢),当作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息为每个划分创建一个map任务,并将map任务分配给TaskTracker执行。
对于map和reduce任务,TaskTracker根据主机核的数量和内存的大小有固定数量的map槽和reduce槽。这里需要强调的是:map任务不是随随便便地分配给某个TaskTracker的,这里有个概念叫:数据本地化(Data-Local)。意思是:将map任务分配给含有该map处理的数据块的TaskTracker上,同时将程序JAR包复制到该TaskTracker上来运行,这叫“运算移动,数据不移动”。而分配reduce任务时并不考虑数据本地化。
任务执行
TaskTracker 申请到新的任务之后,就要在本地运行了。首先,是将任务本地化(包括运行任务所需的数据、配置信息、代码等),即从 HDFS 复制到本地。调用localizeJob()完成的。对于使用 Streaming和 Pipes 创建 Map 或者 Reduce 程序的任务,Java 会把 key/value 传递给外部进程,然后通过用户自定义的 Map 或者Reduce 进行处理,然后把 key/value 传回到 Java 中。其实就好像是 TaskTracker 的子进程在处理 Map 和 Reduce 代码一样。
更新任务执行进度和状态
进度和状态是通过 heartbeat(心跳机制)来更新和维护的。来对于 Map Task,进度就是已处理数据占所有需要数据的百分比。对于 Reduce Task,情况就有点复杂,包括3 部分,拷贝中间结果文件(负责阶段)、排序阶段、Reduce 调用,每部分占 1/3。
TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉JobTracker它依然在运行,同时心跳中还携带着很多的信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户。
作业完成
当 Job 完成后,JobTracker 会收一个 Job Complete 的通知,并将当前的 Job 状态更新为 Successful,同时 JobClient也会轮循获知提交的 Job已经完成,将信息显示给用户。最后,JobTracker 会清理和回收该 Job 的相关资源,并通知 TaskTracker 进行相同的操作(比如删除中间结果文件)。