Hadoop调度算法CapacityScheduler源码分析(1)


核心算法流程

当某个tasktracker上出现空闲slot时,调度器依次选择一个queue、(选中的queue中的)job、(选中的job中的)task,并将该slot分配给该task。下面介绍选择queue、job和task所采用的策略:

(1)选择queue:将所有queue按照资源使用率(numSlotsOccupied/capacity)由小到大排序,依次进行处理,直到找到一个合适的job。

(2)选择job:在当前queue中,所有作业按照作业提交时间和作业优先级进行排序(假设开启支持优先级调度功能,默认不支持,需要在配置文件中开启),调度依次考虑每个作业,选择符合两个条件的job:[1] 作业所在的用户未达到资源使用上限 [2] 该TaskTracker所在的节点剩余的内存足够该job的task使用。

(3)选择task,同大部分调度器一样,考虑task的locality和资源使用情况。(即:调用JobInProgress中的obtainNewMapTask()/obtainNewReduceTask()方法)

参考自:http://dongxicheng.org/mapreduce/hadoop-capacity-scheduler/

CapacityScheduler辅助类

类的静态结构

CapacityTaskScheduler是核心类,我们先看它的辅助类

CapacityTaskScheduler的辅助类都是内部类,下面逐个描述:

(1)TaskSchedulingMgr类

它是任务调度管理,抽象类,包括三个成员:

protected CapacityTaskScheduler scheduler;
protected TaskType type = null;
privateList queuesForAssigningTasks =  
                                newArrayList();

       其中,scheduler是调度器对象,在构造函数中传入;type代表任务类型,在TaskSchedulingMgr的实现类MapSchedulingMgr和ReduceSchedulingMgr中分别取值为TaskType.Map和TaskType.Reduce。第三个重要的变量是queuesForAssigningTasks,这是即将要分配任务的队列,变量中的队列对象按照:正在运行的任务数/容量,进行排序。

    TaskSchedulingMgr的抽象方法有:

obtainNewTask(TaskTrackerStatus, JobInProgress, boolean)
getSlotsOccupied(JobInProgress)
getClusterCapacity()
getSlotsPerTask(JobInProgress)
getRunningTasks(JobInProgress)
getPendingTasks(JobInProgress)
getNumReservedTaskTrackers(JobInProgress)
hasSpeculativeTask(JobInProgress, TaskTrackerStatus)

抽象方法到它的实现类去讨论。

其它普通方法:

hasSufficientReservedTaskTrackers(JobInProgress)

 简单方法,检查 tasktracker数是否大于等于改作业未执行的任务数。

getOrderedQueues():得到所有队列的名称的串,主要用于测试。

initialize(Map)

初始化队列信息,并排序(sortQueues)

sortQueues()

对queuesForAssigningTasks成员排序,比较器为MapQueueComparator和ReduceQueueComparator。

divideAndCeil(inta, int b): a除b正向取整。

TaskLookupResult getTaskFromQueue(TaskTracker,

int,CapacitySchedulerQueue,boolean)

这是一个非常重要的方法,这个方法从一个队列中取出一个task,返回的TaskLookupResult对象包含:

 private LookUpStatus lookUpStatus;
 private Task task;
 private JobInProgress job;

其中task才是关键信息,当然,task可能为空。具体的实现逻辑是:

(1)在队列中的选一个job,检查是否是RUNNING状态的;

如果不是,重复(1),否则继续;

(2)检查队列是否超出最大slot限制,或者用户的slot占有量是否超过限定,如果是则回到(1),否则继续;

(3)检查tasktracker是否能满足作业的内存需求,如果满足则从作业中挑选一个任务,如代码:

TaskLookupResult tlr = obtainNewTasktask(TrackerStatus,j,assignOffSwitch); 

     obtainNewTask()函数负责从job中未运行的任务挑选一个,该函数的具体细节容稍后讨论。 如果没能从job中找到适合的任务,则回到(1),否则成功,返回结果,结束。

如果tasktracker的内存不能满足作业的内存需求,那么调度器会判断该作业是否有待运行的任务或者保留的tasktracker是否足够。如果是则回到(1),否则,将该tasktracker保留,退出函数。这里有一个机制:CapacitySecheduler在调度过程中考虑作业的内存需求,但是,当tasktracker内存无法满足job的内存需求时,系统不会把它直接放弃该tasktracker,而是将它保留给该作业,也就是将该作业的tasktracker的slot登记到job的名下,这样做是为了使该作业不至于饿死。

assignTasks(TaskTrackertaskTracker,

int availableSlots,

 boolean 

assignOffSwitch)

输入参数:tasktracker;
 availableSlots   是tasktracker剩余的Map或Reduce的slot数;
          assignOffSwitch  为false表示局部性,true表示非局部性。

分两种情况,第一是,看taskTracker是否已经被预定了:

JobInProgress job =taskTracker.getJobForFallowSlot(type);

如果job不为空,这表示taskTracker的slot被job预定了,这时做以下工作:

如果availableSlots大于等于每个task所需的slot数,那么taskTracker释放预留的slot,并从job中挑选一个任务在taskTracker上运行,退出。否则,重新预留taskTracker的slot给job,并返回内存匹配失败的TaskLoogupResult对象。

第二种情况,如果taskTracker没有被预留,那么,从备选的队列集合中寻找适合的队列,并从队列中挑选适合的task,选出来的task必须是三种类型中的一种:LOCAL_TASK_FOUND(具有本地局部性),OFF_SWITCH_TASK_FOUND(不具有局部性)或者TASK_FAILING_MEMORY_REQUIREMENT(挑选的任务内存需求得不到满足)。挑选过程的执行代码是:

askLookupResult tlr =getTaskFromQueue(taskTracker, availableSlots,
queue, assignOffSwitch);
TaskLookupResult.LookUpStatus lookUpStatus =tlr.getLookUpStatus();

如果以上三种都不是,则换一个队列,继续找。如果最终还没有找到,返回任务查找失败。

printQueues()

打印作业队列信息,用于调试。

hasSpeculativeTask(TaskInProgress[], float,TaskTrackerStatus)

检查tasktracker是否已经被某个作业预留了。

(2)MapSchedulingMgr/ReduceSchedulingMgr

      两个类都继承自TaskSchedulingMgr,实现了抽象方法,我们先讨论抽象方法在类MapSchedulingMgr中的实现:

obtainNewTask(TaskTrackerStatus,JobInProgress, boolean)

这个函数是针对TaskTracker,从job中挑选一个task,boolean是表示是否要满足局部性的要求。首先,试图查找本地任务;其次,如果失败则找非本地任务,最后返回结果。

下面几个函数都是从简单的读取相应的变量,比较简单所以不讨论。

getClusterCapacity()        读取集群的容量,即slot数

getRunningTasks(JobInProgress)    读取作业正在运行的任务数

getPendingTasks(JobInProgress)    读取作业等待运行的任务数

getSlotsPerTask(JobInProgress)      每个任务所需要的slot数

getNumReservedTaskTrackers(JobInProgress)       为作业预留的taskTracker数

hasSpeculativeTask(JobInProgress,TaskTrackerStatus)                   作业在taskTracker上是否有预留任务。

下面讨论ReduceSchedulingMgr类。

obtainNewTask(TaskTrackerStatus, JobInProgress,boolean)

它的实现看上去比较简单,只有十行以内的代码。

ClusterStatus clusterStatus =
scheduler.taskTrackerManager.getClusterStatus();
int numTaskTrackers = clusterStatus.getTaskTrackers();
Task t =job.obtainNewReduceTask(taskTracker, numTaskTrackers,
             scheduler.taskTrackerManager.getNumberOfUniqueHosts());
 return (t != null) ? TaskLookupResult.getTaskFoundResult(t, job) :
       TaskLookupResult.getNoTaskFoundResult();

你可能感兴趣的:(hadoop)