TaskScheduler是MapReduce中的任务调度器。在MapReduce中,JobTracker接收JobClient提交的Job,将它们按InputFormat的划分以及其他相关配置,生成若干个Map和Reduce任务。

然后,当一个TaskTracker通过心跳告知JobTracker自己还有空闲的任务Slot时,JobTracker就会向其分派任务。具体应该分派一些什么样的任务给这台TaskTracker,这就是TaskScheduler所需要考虑的事情。

TaskScheduler工作在JobTracker上。在JobTracker启动时,根据配置“mapred.jobtracker.taskScheduler”选定一个TaskScheduler的派生类实例作为任务调度器。任务的分派配由JobTracker的调度框架和TaskScheduler的具体调度策略配合完成。简单来说:
1、JobTracker通过一种Listener机制,将Job的变化情况同步给TaskScheduler。然后TaskScheduler就按照自己的策略将需要调度的Job管理起来;
2、在JobTracker需要向TaskTracker分派任务时,调用TaskScheduler的assignTask()方法来获得应当分派的任务;

listener

首先,JobClient提交的Job被JobTracker维护在一个Map结构中,Job的所有状态变更都会在这个结构中体现。但是Job中的任务的分派却跟这个结构无关,因为分派任务是TaskScheduler的事情,显然不能通过直接操作JobTracker的数据结构来完成。
为此,JobTracker中提供了一套JobInProgressListener的机制。当Map结构中的Job发生变化时(增、删、改),JobTracker就会回调已注册的JobInProgressListener的相应方法。TaskScheduler就使用它来监听JobTracker上Job的变化。
抽象类JobInProgressListener有如下方法:
void jobAdded(JobInProgress job);
void jobRemoved(JobInProgress job);
void jobUpdated(JobChangeEvent event);

它的子类主要有JobQueueJobInProgressListener和EagerTaskInitializationListener,如下图。

TaskScheduler在初始化时会向JobTracker注册若干个JobInProgressListener,如下图(JobQueueTaskScheduler是Hadoop默认的TaskScheduler实现)。

当JobTracker接收到新的Job时,会回调每个已注册的JobInProgressListener的jobAdded()方法。

同样,当Job被移除或者发生更新时,jobRemoved()或jobUpdated()会被回调。

其中,jobUpdated()需要传递一个JobChangeEvent,用于描述Job的变化。这里面主要包含三种状态变化:运行状态改变、优先级改变、开始时间改变。

关于Job的运行状态,它包括PREP、RUNNING、FAILED、KILLED、SUCCEEDED五种。其中,PREP状态是Job的Setup任务执行完成之前的状态,JobTracker接收到Job后,需要等Job的Setup任务执行完成之后才能分派Job的Map和Reduce任务。而RUNNING、FAILED、SUCCEEDED则对应Job的执行中、执行失败和执行成功,这应该是很好理解的;而KILLED状态就是用户手动将Job杀死后的状态。

一般来说,Job的运行状态改变主要是由MapReduce框架来触发的,Job的生老病死都是随着框架而运转。而Job的运行状态改变成KILLED、以及Job的优先级改变和Job的开始时间改变,则都是由用户来发起的(可以通过JobClient来提交相应的请求)。

下面看一下MapReduce中默认的TaskScheduler--JobQueueTaskScheduler--的做法。
JobQueueTaskScheduler是一个按优先级顺序选择Job的调度器。它在初始化时会向JobTracker注册JobQueueJobInProgressListener、EagerTaskInitializationListener两个Listener:

JobQueueJobInProgressListener比较简单,其成员只有一个Map类型的jobQueue--按优先级顺序排列的Job队列。Map的KEY是JobSchedulingInfo,它包含三个信息:
1、priority。Job的优先级,第一排序因子。由“mapred.job.priority”配置;
2、startTime。Job开始时间、第二排序因子。Job实际开始的时间,也可以在Job执行过程中被用户改写;
3、id。也就是JobId,非排序因子;
回调函数jobAdded()会将新的Job加入到jobQueue中,并排序;而jobUpdated()则会根据Job开始时间或优先级的变化,调整它在jobQueue中的排序。并且当Job的运行状态变为中止状态(KILLED、FALED、SUCCESSED)时会将Job从jobQueue中移除;而jobRemoved()则是什么都不做(因为移除Job的工作已经在jobUpdated()里面做了)。

EagerTaskInitializationListener是一个用于初始化Job的Listener。它维护了若干个工作线程(数目由“mapred.jobinit.threads”配置,默认为4)来初始化Job。回调函数jobAdded()会将Job提交给这些工作线程。
对Job的初始化是通过调用JobTracker.initJob()方法来实现的(注意,就算不使用EagerTaskInitializationListener,也应该通过某种途径来调用这个方法,以便初始化Job),其主要工作就是为Job生成一堆的任务(在这一步之前,只有Job,没有Task):

1、获取JobClient提交的Splits信息

2、为每一个Split创建一个对应的Map任务,并按照Split的Location信息将这些Map任务归类,为之后的任务分派做准备(这里将网络视作树状的拓扑结构,每个运行TaskTracker的机器都对应到树的叶子节点上,而非叶子节点就是连接它们的交换机。然后,每个Map任务就挂在其Location对应的叶子节点及其所有祖先节点上。这样,在分派任务的时候,就可以从TaskTracker对应的叶子节点出发,逐步向上,从而寻找到一个距离该TaskTracker最近的尚未分派的Map任务);

3、根据Job的配置“mapred.reduce.tasks”,创建若干个Reduce任务:

4、额外创建两个Cleanup任务和两个Setup任务,用于执行OutputCommit中的操作。为什么Cleanup和Setup任务会各有两个呢?因为它们需要以Map任务或是Reduce任务的身份被分派(其区别是占用Map的Slot或是Reduce的Slot),所以同一个东西有了两种不同的形态;在这一步Job初始化完成之后,Job下的Task才能被分派给TaskTracker。(然后,首先只能分派一个Setup任务,并且等它执行完成之后,其他的Map和Reduce任务才能被分派,EagerTaskInitializationListener完成了对Job的初始化,很多TaskScheduler都会使用到它。
此外,在EagerTaskInitializationListener中,提交给工作线程初始化的Job会先放到一个按优先级排序的Queue里面,同样是以priority为第一排序因子、以startTime为第二排序因子。然后jobUpdated()会根据Job的priority或startTime的变化来调整Job在Queue中的排序;而jobRemoved()则会将Job从Queue中移除。
不过这里的优先级其实作用不是很大,因为相比于Job的执行时间,Job的初始化时间基本上可以忽略不计。(Job在Queue中发生竞争的可能性不大。)

任务分配

在TaskTracker通过心跳向JobTracker告知尚有空余的任务Slot时,JobTracker就会考虑向TaskTracker分派任务。
虽说Task的分派主要由TaskScheduler来负责,但是JobTracker会优先进行Setup和Cleanup任务的分派,如果有Job需要分派Setup或Cleanup任务,JobTracker会优先分派它们(而不考虑Job的优先级)。不过一般需要执行的Setup和Cleanup任务数量很少,而且执行时间很短,所以影响不大。重头戏还是在后面,JobTracker调用TaskScheduler的assignTask()方法,来分派配任务。

JobTracker的heartbeat方法,如下图

具体的分派策略就跟具体的TaskScheduler相关了。还是以默认的JobQueueTaskScheduler为例:
1、顺序遍历JobQueueJobInProgressListener.jobQueue中的每一个Job(前面说过,已按优先级排好序),选择第一个RUNNING状态的Job(注意,jobQueue中可能有PREP状态的Job,它们尚未被EagerTaskInitializationListener初始化好),尝试从中分派任务。如果没有任务可以分派,则再选择下一个RUNNING状态的Job;
2、每次从一个Job中分派Map任务时,遵序如下步骤:
2.1、优先分派执行失败后等待重试的任务。对于这些需要重试的任务,并不考虑Location问题(因为要保证这些重试任务的优先顺序。另外,这个任务第一次分派的时候已经考虑了Location问题,但是失败了。如果重试还要考虑Location,那么这个任务可能很又会被分派到之前执行失败过的那台TaskTracker上。这显然是不好的);
2.2、从未分派的Map任务中进行分派,根据Location优先选择输入Split离TaskTracker最近的Map任务(前面已经介绍了,Map任务按照其Location信息放到网络树中);
2.3、如果启用了推断执行(由“mapred.map.tasks.speculative.execution”配置,默认为true),则尝试对已分派的Task进行再次分派(如果该Map执行进度比较落后的话);
3、Reduce任务的分派逻辑也是类似的,但是不需要考虑Location问题;

总的来说,任务分派是要将TaskTracker上空闲的Slot都填满,让MapReduce集群的计算能力尽量投入到Job的执行中。但是,在一次assignTask()的过程中并不总是一口气把TaskTracker上空闲的Slot全部填满,里面会有一些break逻辑。遇到下列情况时,分派流程将会break:
1、对于非Data-Local、非Rack-Local(同一机架)的Map任务,最多只会分派一个;
2、Reduce任务最多只会分派一个;
3、JobQueueTaskScheduler会为每个TaskTracker预留少量的计算能力,专为最高优先级的Job服务(也就是jobQueue中第一个能够分派任务的Job)。如果当前TaskTracker用完了非预留的计算能力,则最多只会分派一个任务(当然,分派的任务一定属于最高优先级的Job,因为分派过程是按Job的优先级顺序来进行的);

break之后,本次分派的任务可能就填不满TaskTracker的空闲Slot了,不过没关系,等这个TaskTracker下一次来心跳的时候,JobTracker又会再给它分派任务。这样的设计使得:
1、Map任务趋于分派到更合适的TaskTracker上。如果没有合适的Map任务分派给当前TaskTracker,则延迟其他Map任务的分派。那么这些延迟分派的Map任务可能可以分派到其他更合适的TaskTracker上;
2、Reduce任务趋于平均分派到每一个TaskTracker上。因为每个TaskTracker每次心跳都最多领走一个Reduce任务,只要每个TaskTracker心跳的频率相当,Reduce任务就能平均分派;

其他调度器

除了前面介绍的默认调度器--JobQueueTaskScheduler,MapReduce中还有提供了几种调度器可供选择。当然,你也可以实现自己的调度器。就像前面所介绍的那样,实现一组Listener、再实现assignTask()方法,基本上也就差不多了。
下面就简要介绍几种调度器:

1、LimitTasksPerJobTaskScheduler
这个调度器比较简单,它继承了JobQueueTaskScheduler,并重写了assignTask()方法。其思想是:给每个Job设置一个执行任务的限额(由“mapred.jobtracker.taskScheduler.maxRunningTasksPerJob”配置)。在以Job优先级顺序分派任务的基础上,如果某个Job分派出去的任务大于任务限额,则暂时跳过它,而考虑优先级更低的任务。如果不存在低优先级的Job、或者低优先级的Job分派不出任务,那么就算高优先级的Job超过了任务限额,也能继续分派任务。
这样一来,就既可以保证高优先级的Job得到较多的计算资源,又避免低优先级的Job过于饥饿。

2、FairScheduler
公平调度,其思想是:让每个Job能够得到公平的资源分配。不过,说起来简单,FairScheduler的实现却是非常复杂的。除了最基本的公平任务分配,还支持按Pool划分Job(每个Pool可以设置不同的配额),还支持在不公平情况下的任务抢占,等等
值得一提的是,FairScheduler也使用了EagerTaskInitializationListener来初始化Job。

3、CapacityScheduler
假设我们有多个为不同应用服务的MapReduce集群。一般来说,为了满足应用的峰值需求,集群总是需要配备足够的计算资源。但是大多数情况下,应用对计算资源的需求往往是远低于峰值需求的,这就带来了很大的资源浪费。于是,我们可以将这些集群合并起来,以期最大限度的提高集群的利用率。
但是简单的集群合并没法保证各个应用所需要的最低计算资源,特别是当多个应用的高峰同时到来时,“倒霉”的应用可能会饿死。而使用CapacityScheduler就能提供这样的保证。CapacityScheduler可以配置若干个Queue,以Queue为单位来设置最低容量保证。

本文参考:http://hi.baidu.com/_kouu/item/23d381ad402ea5716cd455d9

Hadoop TaskScheduler源码分析的更多相关文章

  1. Hadoop RPC源码分析

    Hadoop RPC源码分析 上一篇文章http://www.cnblogs.com/dycg/p/rpc.html 讲了Hadoop RPC的使用方法,这一次我们从demo中一层层进行分析. RPC ...

  2. [Hadoop] - TaskTracker源码分析(状态发送)

    TaskTracker节点向JobTracker汇报当前节点的运行时信息时候,是将运行状态信息同心跳报告一起发送给JobTracker的,主要包括TaskTracker的基本信息.节点资源使用信息.各 ...

  3. Hadoop TextInputFormat源码分析

    from:http://blog.csdn.net/lzm1340458776/article/details/42707047 InputFormat主要用于描述输入数据的格式(我们只分析新API, ...

  4. [Hadoop] - TaskTracker源码分析

    在Hadoop1.x版本中,MapReduce采用master/salve架构,TaskTracker就是这个架构中的slave部分.TaskTracker以服务组件的形式存在,负责任务的执行和任务状 ...

  5. [Hadoop] - TaskTracker源码分析(TaskTracker节点健康状况监控)

    在TaskTracker中对象healthStatus保存了当前节点的健康状况,对应的类是org.apache.hadoop.mapred.TaskTrackerStatus.TaskTrackerH ...

  6. Hadoop2源码分析-准备篇

    1.概述 我们已经能够搭建一个高可用的Hadoop平台了,也熟悉并掌握了一个项目在Hadoop平台下的开发流程,基于Hadoop的一些套件我们也能够使用,并且能利用这些套件进行一些任务的开发.在Had ...

  7. 《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(叔篇)——TaskScheduler的启动

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  8. Spark源码分析(三)-TaskScheduler创建

    原创文章,转载请注明: 转载自http://www.cnblogs.com/tovin/p/3879151.html 在SparkContext创建过程中会调用createTaskScheduler函 ...

  9. Hadoop RCFile存储格式详解(源码分析、代码示例)

    RCFile   RCFile全称Record Columnar File,列式记录文件,是一种类似于SequenceFile的键值对(Key/Value Pairs)数据文件.   关键词:Reco ...

随机推荐

  1. 使用SVG + CSS实现动态霓虹灯文字效果

    效果图: 原理:多个SVG描边动画使用不同的animation-delay即可! 对于一个形状SVG元素或文本SVG元素,可以使用stroke-dasharray来控制描边的间隔样式,并且可以用str ...

  2. Sql server 备份及还原

    --最常用的几种备份方法: --数据备份----------------------- --数据库级:完整数据库备份 差异数据库备份 --文件级: 完整文件备份 差异文件备份 --日志备份------ ...

  3. HBase 官方文档

    HBase 官方文档 Copyright © 2010 Apache Software Foundation, 盛大游戏-数据仓库团队-颜开(译) Revision History Revision ...

  4. SharePoint 2013上传AI格式文件,再次下载后变成了PS格式文件

    问题: SharePoint 2013上传AI格式文件,再次下载后变成了PS格式文件 需要下载副本才能显示AI格式 解决办法有两个: 第一种,在客户端机器1. Click Start, click R ...

  5. [hdu2159]FATE二维多重背包(背包九讲练习)

    解题关键:二维约束条件,只需加一维状态即可. 转移方程:$f[j][k] = \max (f[j][k],f[j - w[i]][k - 1] + v[i])$ #include<bits/st ...

  6. error C2872: “flann”: 不明确的符号 --- PCL 与OpenCV2 的flann命名空间冲突问题的解决方法

    error C2872: "flann": 不明确的符号 - PCL 与OpenCV2命名空间冲突问题的解决方法 error C2872: "flann" 如果 ...

  7. Python实现R包brainwaver中的compute.FDR函数

    FDR(false discovery rate),是统计学中常见的一个名词,翻译为伪发现率,其意义为是 错误拒绝(拒绝真的(原)假设)的个数占所有被拒绝的原假设个数的比例的期望值. compute. ...

  8. URAL 1297 Palindrome (后缀数组+RMQ)

    题意:给定一个字符串,求一个最长的回回文子串,多解输出第一个. 析:把字符串翻转然后放到后面去,中间用另一个字符隔开,然后枚举每一个回文串的的位置,对第 i 个位置,那么对应着第二个串的最长公共前缀, ...

  9. ES Docs-1:Installation Elasticsearch-2.3.1

    installation Elasticsearch requires at least Java 7. Specifically as of this writing, it is recommen ...

  10. hdu1076

    #include<iostream> using namespace std; int main() { int cases; int k; cin>>cases; while ...