通过前面篇章,我们对于MapReduce已经有了不错的了解,由于现在几乎没有使用MapReduce去开发业务需求的场景,甚至MapReduce这个引擎都随着时代变化,快要完全被淘汰了,所以我们就不去水看使用MapReduce编程相关的东西,而是把重点放到一些我们比较感兴趣的点上。
今天我们先来看看MRv1计算框架的核心设计实现。
首先,我们梳理 MR Job 的生命周期流程如下:
通过前面篇章,我们知道MRv1是基于Hadoop1.x的架构的,如下图所示:
左边是我们很熟悉的HDFS的架构,右边则是MapReduce架构设计。
我们继续深入涉及的核心组件:
JobTracker本质是一个后台服务进程,启动之后,会一直监听并接收来自各个TaskTracker发送的心跳信息,这里面包含节点资源使用情况和任务运行情况等信息。JobTracker会将这些信息统一保存起来,并将这些信息告诉任务调度器,而调度器会在资源出现空闲时,选择合适的任务使用这些资源。
这种将新作业通知TaskScheduler的操作,类似于观察者设计模式。
JobTracker的核心功能是作业调度和资源监控。
JobTracker在其内部以“三层多叉树”的方式描述和跟踪每个作业的运行状态。
作业被抽象成三层,从上往下依次为:作业监控层、任务监控层和任务执行层。
JobTracker将每次尝试运行一次任务称为“任务运行尝试”,而对应的任务运行实例称为Task Attempt(TA)。当任何一个Task Attempt运行成功后,其上层对应的TaskInProgress会标注该任务运行成功;而当所有的TaskInProgress运行成功后, JobInProgress则会标注整个作业运行成功。
JobTracker不断接收各个TaskTracker周期性发送过来的资源量和任务状态等信息,并综合考虑TaskTracker(所在DataNode)的数据分布、资源剩余量、作业优先级、作业提交时间等因素,为TaskTracker分配最合适的任务。
Hadoop引入了 slot 概念表示各个节点上的计算资源。为了简化资源管理,Hadoop将各个节
点上的资源(CPU、内存和磁盘等)等量切分成若干份,每一份用一个slot表示,同时规定一个Task
可根据实际需要占用多个slot。
JobTracker容错的关键技术点是如何保存和恢复作业的运行时信息。
从作业恢复粒度角度看,当前存在三种不同级别的恢复机制,级别由低到高依次是作业级别、任务级别和记录级别,其中,级别越低,实现越简单,但造成的资源浪费越严重。
作业恢复的机制处理比较简单。每个新的作业(Job)会在JobTracker的工作目录下为该作业创建一个以该作业的JobId为命名的目录,目录底下放该作业的Job-info和JobToken文件。如果该作业成功运行结束,那么就会在作业的Cleanup工作中删除掉该文件夹。
所以,当某个时刻JobTracker如果突然因为故障重启了,那么该工作目录下如果JobId工作目录,就说明重启之前还有作业未运行结束(因为运行结束的Job都会把自己的目录清除掉),此时就会把目录中包含的作业重新提交运行,并且JobTracker会把这些重新提交运行的Job的Id信息通过心跳信息的回复告知TaskTracker。
那些之前就已经运行在TaskTracker上的任务就是根据TaskID和JobID来更新JobTracker中的作业和任务的信息状态的。原本就正在运行的任务仍然能够正常的更新JobTracker。已经运行结束的Task会把新提交的作业的Task直接更新为运行结束。
TaskTracker是JobTracker与Task之间的“中间人”
TaskTracker与JobTracker和Task之间采用了RPC协议进行通信。对于TaskTracker和JobTracker而言,它们之间采用InterTrackerProtocol协议,其中,JobTracker扮演RPC Server的角色,而TaskTracker扮演RPC Client的角色;对于TaskTracker与Task而言,它们之间采用TaskUmbilicalProtocol协议,其中,TaskTracker扮演RPC Server的角色,而Task扮演RPC Client的角色。
关于RPC可以看这篇文章。
TaskTracker核心功能是汇报心跳和执行命令。
TaskTracker周期性地将所在节点上各种信息通过心跳机制汇报给JobTracker。这些信息包括两部分:机器级别信息,如节点健康状况、资源使用情况等;任务级别信息,如任务执行进度、任务运行状态、任务Counter值等。
心跳是Jobtracker和Tasktracker的桥梁,它实际上是一个RPC函数,Tasktracker周期性的调用该函数汇报节点和任务状态信息,从而形成心跳。在Hadoop中,心跳主要有三个作用:
- 判断Tasktracker是否活着
- 及时让Jobtracker获取各个节点上的资源使用情况和任务运行状态
- 为Tasktracker分配任务
Tasktracker周期性的调用RPC函数heartbeat向Jobtracker汇报信息和领取任务。
JobTracker与TaskTracker之间采用了Pull而不是Push模型,是JobTracker不会主动向TaskTracker发送任何信息,而是由TaskTracker主动通过心跳领取属于自己的消息,JobTracker只能通过心跳应答的形式为各个TaskTracker分配任务。
JobTracker收到TaskTracker心跳信息后,会根据心跳信息和当前作业运行情况为该TaskTracker下达命令,主要包括启动任务(LaunchTaskAction)、提交任务(CommitTaskAction)、杀死任务(KillTaskAction)、杀死作业(KillJobAction)和重新初始化(TaskTrackerReinitAction)5种命令。其中,过程比较复杂的是启动任务。为了防止任务之间的干扰,TaskTracker为每个任务创建一个单独的JVM,并有专门的线程监控其资源使用情况,一旦发现超量使用资源就直接将其杀掉。
TaskTracker负责执行来自JobTracker的各种命令,并将命令执行结果定时汇报给它。在一个Hadoop集群中,TaskTracker数量通常非常多,设计合理的TaskTracker容错机制对于及时发现存在问题的节点显得非常重要。
Hadoop提供了三种TaskTracker容错机制:
今天深入梳理了MRv1的核心设计实现,本篇内容主要是帮助大家拓宽一下视野。