1) 4个独立的实体
客户端: 提交MapReduce作业
jobtracker: 协调作业的运行
tasktracker: 运行作业划分后的任务
HDFS: 用来在其他实体间共享作业文件
2) 作业的提交
JobClient的runjob方法,用于创建JobClient实例并调用其submitJob()方法,提交作业后,runjob()每秒轮询作业进度,并打印
submitJob()方法做的事情: page(188)
3) 作业的初始化
当JobTracker接收到对其submitJob()方法调用后,会把这个调用放入一个内部队列中,交由作业调度器(job scheduler)调度,并对其进行初始化。作业调用度器先从HDFS中获取JobClient已经计算好的分片(步骤6),然后为每个分片创建一个map任务,然后创建相应数量的要运行的reduce任务,任务在此时被指定ID
4) 任务的分配
tasktracker运行一个简单的循环定期发送“心跳”给jobtracker,心跳告知jobtracker,tasktracker是否还存活,充当两者的消息通道。心跳还会表示,tasktracker它是否已经准备好运行新的任务,如果是,jobtracker会给它分配一个任务,并通过心跳与tasktracker进行通信
任务槽: tasktracker有固定的任务槽,如,一个tasktracker可能同时运行2个map任务和2个reduce任务。数量由tasktracker核的数量和内存决定。默认调度器会在处理reduce任务槽之前,会填满空闲的map任务槽
5) 任务的运行
tasktracker被分配了一个任务,通过HDFS把作业的jar文件复制到tasktracker所在的文件系统,实现jar文件本地化。将应用程序需要的全部文件从分布式缓存复制到本地磁盘(步骤8)。为任务新建一个本地工作目录,解压jar文件,新建一个TaskRunenr实例运行任务。TaskRunner启动一个新的JVM来运行每个任务
6) 作业的完成
当jobtracker收到作业最后一个任务已完成的通知,便把作业的状态设置为“成功”,在JobClient查询状态后,便知道任务已经完成,于是JobClient打印一条消息通知用户,然后从runjob()方法返回。
7) 作业调度器
通过设置mapred.job.priority属性或JobClient的setJobPriority(0方法设置执行优先级,默认是FIFO,优先级不支持抢占,高优先级的作业仍然会等待已经运行的作业,会被阻塞
a. Fair Scheduler
可以多个作业一起运行,公平共享集群;支持抢占
b. Capacity Scheduler
8) shuffle和排序
系统执行排序的过程--将map的输出作为输入传给reducer,称为shuffle。
map端: 每个map任务都有一个环形内存缓冲区,用于存储任务的输出。一旦缓冲区内容达到阈值,一个后台线程便开始把内容写到(spill)磁盘中
combiner会在输出文件写到磁盘之前运行
写磁盘时压缩map输出是比较好
reduce端: map输出文件位于运行map任务的tasktracker的本地磁盘;每个map任务的完成时间可能不同,因此只要有一个任务完成,reduce任务就开始复制其输出,这是reduce任务的复制阶段,reduce任务有复制线程,可以并行取得map输出;当map输出较小时,会放在reduce端的内存中;
复制完所有map输出,reduce任务进入排序阶段(确切说应该是合并阶段,排序是在map端进行的),这个阶段合并map输出,维持其顺序排序,循环进行
最后,将数据输入reduce函数