上一篇文章浅析了FairScheduler的assignTasks()方法,介绍了FairScheduler任务调度的原理。略过了最后一步通过JobScheduler获取Task时调用JobInProgress的五个方法:obtainNewNodeLocalMapTask(),obtainNewNodeOrRackLocalMapTask(),obtainNewMapTask(),obtainNewReduceTask()。这篇文章将对这四个方法进行简单的源代码解析。obtainNewNodeLocalMapTask(),obtainNewNodeOrRackLocalMapTask(),obtainNewMapTask()这三个方法都是选择一个Map任务,其内部调用的方法也是一样的,都是obtainNewMapTaskCommon()方法,不同的只是maxCacheLevel参数值不同:

obtainNewNodeLocalMapTask():maxCacheLevel==1;

obtainNewNodeOrRackLocalMapTask:maxCacheLevel==maxLevel;

obtainNewMapTask:maxCacheLevel==anyCacheLevel(maxLevel+1)。

下面着重分析obtainNewMapTaskCommon()方法。

1.JobInProgress.obtainNewMapTaskCommon():

public synchronized Task obtainNewMapTaskCommon(
TaskTrackerStatus tts, int clusterSize, int numUniqueHosts,
int maxCacheLevel) throws IOException {
if (!tasksInited) {
LOG.info("Cannot create task split for " + profile.getJobID());
try { throw new IOException("state = " + status.getRunState()); }
catch (IOException ioe) {ioe.printStackTrace();}
return null;
} int target = findNewMapTask(tts, clusterSize, numUniqueHosts, maxCacheLevel,
status.mapProgress());
if (target == -1) {
return null;
} Task result = maps[target].getTaskToRun(tts.getTrackerName());
if (result != null) {
addRunningTaskToTIP(maps[target], result.getTaskID(), tts, true);
// DO NOT reset for off-switch!
if (maxCacheLevel != NON_LOCAL_CACHE_LEVEL) {
resetSchedulingOpportunities();
}
}
return result;
}

首先判断Job是否初始化,即tasksInited是否为true,未初始化则不调度任务。接着调用findNewMapTask()方法获取一个新的Map任务,同时将maxCacheLevel参数传递过去,该参数的作用是选择不同LocalLevel的Map任务。下面看看findNewMapTask()方法。

2.JobInProgress.findNewMapTask():

首先介绍下该方法的返回值,该方法不是直接返回一个MapTask,而是返回一个maps[]数组的索引值,这个maps[]数组在Job初始化时创建,存放该Job所有的Map任务,根据索引值就可以知道对应的MapTask。

if (numMapTasks == 0) {
if(LOG.isDebugEnabled()) {
LOG.debug("No maps to schedule for " + profile.getJobID());
}
return -1;
}

首先判断该Job的Map任务数是否为0,numMapTasks是在Job进行初始化(initTasks()方法)时根据输入文件的分片数确定的,即一个Split对应一个Map任务。该值为0表示该Job没有Map任务,所以返回-1,即没有满足条件的Map任务。

String taskTracker = tts.getTrackerName();
TaskInProgress tip = null; //
// Update the last-known clusterSize
//
this.clusterSize = clusterSize; if (!shouldRunOnTaskTracker(taskTracker)) {
return -1;
}

判断是否能够在该TT上运行任务,主要根据该Job在该TT上是否有过失败的任务来判断。下面看看该方法。

3.JobInProgress.shouldRunOnTaskTracker():

private boolean shouldRunOnTaskTracker(String taskTracker) {
//
// Check if too many tasks of this job have failed on this
// tasktracker prior to assigning it a new one.
//
int taskTrackerFailedTasks = getTrackerTaskFailures(taskTracker);
if ((flakyTaskTrackers < (clusterSize * CLUSTER_BLACKLIST_PERCENT)) &&
taskTrackerFailedTasks >= maxTaskFailuresPerTracker) {
if (LOG.isDebugEnabled()) {
String flakyTracker = convertTrackerNameToHostName(taskTracker);
LOG.debug("Ignoring the black-listed tasktracker: '" + flakyTracker
+ "' for assigning a new task");
}
return false;
}
return true;
}
  private int getTrackerTaskFailures(String trackerName) {
String trackerHostName = convertTrackerNameToHostName(trackerName);
Integer failedTasks = trackerToFailuresMap.get(trackerHostName);
return (failedTasks != null) ? failedTasks.intValue() : 0;
}

该方法从Job中保存的trackerToFailuresMap队列中获取该TT上所有的失败任务数。提一下,trackerToFailuresMap队列信息也是在TT通过心跳向JT时更新的,即updateTaskStatus()方法。flakyTaskTrackers值记录该Job在多少个TT上面失败的任务数大于maxTaskFailuresPerTracker(即一个Job在一个TT上可允许失败的最大数),当一个Job在一个TT上拥有的失败任务数大于maxTaskFailuresPerTracker时则表示该Job不可再在该TT上执行任何任务,但是当一个Job在超过(clusterSize * CLUSTER_BLACKLIST_PERCENT)个TT上失败的话,则不去考虑该Job是否在该TT上失败,因为可能是Job自身的问题,而非单个TT的问题。总之该方法根据Job的失败任务信息来判断是否应该在一个TT上执行任务。

接着返回到JobInProgress.findNewMapTask()方法。

4.JobInProgress.findNewMapTask():

// Check to ensure this TaskTracker has enough resources to
// run tasks from this job
long outSize = resourceEstimator.getEstimatedMapOutputSize();
long availSpace = tts.getResourceStatus().getAvailableSpace();
if(availSpace < outSize) {
LOG.warn("No room for map task. Node " + tts.getHost() +
" has " + availSpace +
" bytes free; but we expect map to take " + outSize); return -1; //see if a different TIP might work better.
}

判断该TT是否有够该Job的Map任务使用的资源,主要是根据该Job已完成的Map任务的输出情况来估算一个Map任务可能的输出大小。

long getEstimatedMapOutputSize() {
long estimate = 0L;
if (job.desiredMaps() > 0) {
estimate = getEstimatedTotalMapOutputSize() / job.desiredMaps();
}
return estimate;
}
protected synchronized long getEstimatedTotalMapOutputSize()  {
if(completedMapsUpdates < threshholdToUse) {
return 0;
} else {
long inputSize = job.getInputLength() + job.desiredMaps();
//add desiredMaps() so that randomwriter case doesn't blow up
//the multiplication might lead to overflow, casting it with
//double prevents it
long estimate = Math.round(((double)inputSize *
completedMapsOutputSize * 2.0)/completedMapsInputSize);
if (LOG.isDebugEnabled()) {
LOG.debug("estimate total map output will be " + estimate);
}
return estimate;
}
}

具体估算方法是根据(该Job的输入大小/已完成的Map任务的输入大小)*(该Job已完成的所有Map任务的总输出大小)*2估算出该Job全部Map任务大概的输出大小,然后除以该Job的Map数量即一个Map任务的可能输出大小(至于这些值的跟新基本都是通过心跳通信)。如果TT上可使用的资源小于该Job一个Map任务可能的输出大小则不能在该TT上执行Map任务。

5.JobInProgress.findNewMapTask():

接下来就是该方法的关键部分,首先看下作者们对该部分的一个注释:

// When scheduling a map task:
// 0) Schedule a failed task without considering locality
// 1) Schedule non-running tasks
// 2) Schedule speculative tasks
// 3) Schedule tasks with no location information // First a look up is done on the non-running cache and on a miss, a look
// up is done on the running cache. The order for lookup within the cache:
// 1. from local node to root [bottom up]
// 2. breadth wise for all the parent nodes at max level
// We fall to linear scan of the list ((3) above) if we have misses in the
// above caches

第一部分主要是说明选择任务的顺序:失败的Task(不去考虑本地性),未运行的任务,推测执行的任务,输入文件没有对应的Location信息的任务。第二部分是说明在选择每个任务时对集群上所有节点的遍历方式:自下往上一次遍历以及从根节点横向遍历。

下面来看第一中选择方式:从失败的任务中选择。

 // 0) Schedule the task with the most failures, unless failure was on this
// machine
tip = findTaskFromList(failedMaps, tts, numUniqueHosts, false);
if (tip != null) {
// Add to the running list
scheduleMap(tip);
LOG.info("Choosing a failed task " + tip.getTIPId());
return tip.getIdWithinJob();
}

failedMaps中存放着所有的失败任务信息,直接调用findTaskFromList()方法从failedMaps中选择一个任务。下面三种方式也都是调用该方法,不同的只是传入的List不同,所以看下findTaskFromList()方法。

6.JobInProgress.findTaskFromList():

private synchronized TaskInProgress findTaskFromList(
Collection<TaskInProgress> tips, TaskTrackerStatus ttStatus,
int numUniqueHosts,
boolean removeFailedTip) {
Iterator<TaskInProgress> iter = tips.iterator();
while (iter.hasNext()) {
TaskInProgress tip = iter.next(); // Select a tip if
// 1. runnable : still needs to be run and is not completed
// 2. ~running : no other node is running it
// 3. earlier attempt failed : has not failed on this host
// and has failed on all the other hosts
// A TIP is removed from the list if
// (1) this tip is scheduled
// (2) if the passed list is a level 0 (host) cache
// (3) when the TIP is non-schedulable (running, killed, complete)
if (tip.isRunnable() && !tip.isRunning()) {
// check if the tip has failed on this host
if (!tip.hasFailedOnMachine(ttStatus.getHost()) ||
tip.getNumberOfFailedMachines() >= numUniqueHosts) {
// check if the tip has failed on all the nodes
iter.remove();
return tip;
} else if (removeFailedTip) {
// the case where we want to remove a failed tip from the host cache
// point#3 in the TIP removal logic above
iter.remove();
}
} else {
// see point#3 in the comment above for TIP removal logic
iter.remove();
}
}
return null;
}

该方法的作用是从一个TaskInProgress列表中选择一个适合在TT上执行的Task》从代码中的注释可以看出选择的前提是Task是可运行的(!failed && (completes == 0),即未失败也未完成),且非正在运行中(no other node is running it),且该Task没有在该TT所在的HOST上有过失败任务(一个Task会存在多个TaskAttempt任务,TaskAttempt听名字就可以知道是一个Task的多次尝试执行,失败了就再来一次,再失败再来,直到超出一个限度才会标志这个Task失败),或者该Task的失败次数大于等于集群中所有的HOST数量(表示该Task在所有HOST都失败过),满足上面三个条件的Task即可返回。后面就是判断是否将该Task从队列中移除,注释给出了三种会移除的情况:该Task已被调度,即被选中;选择的Task的本地化等级是NODE;该Task处于不可运行状态(运行中,或者完成,或者被kill掉了)。了解了该方法的原理则后面的内容就简单了。下面回到JobInProgress.findNewMapTask()方法。

7.JobInProgress.findNewMapTask():

tip = findTaskFromList(failedMaps, tts, numUniqueHosts, false);
if (tip != null) {
// Add to the running list
scheduleMap(tip);
LOG.info("Choosing a failed task " + tip.getTIPId());
return tip.getIdWithinJob();
}

依旧看这段代码,当findTaskFromList()方法成功返回一个Task后,需要将该Task添加到运行中的队列去,

protected synchronized void scheduleMap(TaskInProgress tip) {

    if (runningMapCache == null) {
LOG.warn("Running cache for maps is missing!! "
+ "Job details are missing.");
return;
}
String[] splitLocations = tip.getSplitLocations(); // Add the TIP to the list of non-local running TIPs
if (splitLocations == null || splitLocations.length == 0) {
nonLocalRunningMaps.add(tip);
return;
} for(String host: splitLocations) {
Node node = jobtracker.getNode(host); for (int j = 0; j < maxLevel; ++j) {
Set<TaskInProgress> hostMaps = runningMapCache.get(node);
if (hostMaps == null) {
// create a cache if needed
hostMaps = new LinkedHashSet<TaskInProgress>();
runningMapCache.put(node, hostMaps);
}
hostMaps.add(tip);
node = node.getParent();
}
}
}

该方法主要将被选中的Task(这里任务都是Map任务)添加到Job中保存运行中任务信息的队列中(nonLocalRunningMaps和runningMapCache),nonLocalRunningMaps保存那些输入文件没有Location信息的Task,而runningMapCache则保存输入文件存在Location的Task。runningMapCache是一个Map,key是Node,而value是一个TaskInProgress对象的集合,说明该Map保持的是一个Node-->其上运行的所有Task的一个映射关系,这里的Node是拥有该Task的输入文件块的所有节点。当有一个Task需要添加到runningMapCache时不仅需要为其建立到Task的输入文件所在的所有Node到该Task的关系,而且分别为其建立输入文件所在Node的父节点到该Task的关系,循环知道遍历的深度等于maxLevel。这样做的好处是可以很方便的知道一个Node上运行的所有Task信息,包括其子节点上运行的Task。继续返回到JobInProgress.findNewMapTask()方法。

接下来就是简单地返回该Task在maps[]数组中的索引值。到这里第一种选择Task的方式完成了,下面看看后面几种方式。

8.JobInProgress.findNewMapTask():

Node node = jobtracker.getNode(tts.getHost());

获取该TT所在的HOST对应的Node对象。

// 1. check from local node to the root [bottom up cache lookup]
// i.e if the cache is available and the host has been resolved
// (node!=null)
if (node != null) {
Node key = node;
int level = 0;
// maxCacheLevel might be greater than this.maxLevel if findNewMapTask is
// called to schedule any task (local, rack-local, off-switch or
// speculative) tasks or it might be NON_LOCAL_CACHE_LEVEL (i.e. -1) if
// findNewMapTask is (i.e. -1) if findNewMapTask is to only schedule
// off-switch/speculative tasks
int maxLevelToSchedule = Math.min(maxCacheLevel, maxLevel);
for (level = 0;level < maxLevelToSchedule; ++level) {
List <TaskInProgress> cacheForLevel = nonRunningMapCache.get(key);
if (cacheForLevel != null) {
tip = findTaskFromList(cacheForLevel, tts,
numUniqueHosts,level == 0);
if (tip != null) {
// Add to running cache
scheduleMap(tip); // remove the cache if its empty
if (cacheForLevel.size() == 0) {
nonRunningMapCache.remove(key);
} return tip.getIdWithinJob();
}
}
key = key.getParent();
} // Check if we need to only schedule a local task (node-local/rack-local)
if (level == maxCacheLevel) {
return -1;
}
}

这一部分是从未运行的Map任务中选择一个可执行的Map任务。首先计算maxLevelToSchedule,该值是maxCacheLevel和maxLevel的较小的值,注释给出的解释是maxCacheLevel(调用该方法传入的参数值)可能会比该Job的maxLevel属性值大,所以选择两者之中最小的值作为选择的最大本地等级值(maxLevelToSchedule)。接下来就是自下往上寻找满足条件的Map任务,知道遍历深度达到maxLevelToSchedule。方法较简单,只是从nonRunningMapCache中选择出对应的Node上所有的未执行的Map任务集合,然后调用同上面一样的findTaskFromList()方法从TaskInProgress集合中选择一个适合在该TT上执行的Map任务,选择一个Map任务之后还是一样的步骤调用scheduleMap()方法将其添加到运行中的队列中。当循环结束之后,如果未选择出一个Map任务,则到下面判断如果level==maxCacheLevel,这里level是循环结束时的值,即level==maxLevelToSchedule,而maxLevelToSchedule==Math.min(maxCacheLevel, maxLevel),那么如果要使level==maxCacheLevel,则maxCacheLevel必须是小于等于maxLevel,从前面三个方法内部调用obtainNewMapTaskCommon()方法时传的maxCacheLevel参数值可以看出,obtainNewNodeLocalMapTask()传的值是1,obtainNewNodeOrRackLocalMapTask()传的值是maxLevel,而obtainNewMapTask传的值是anyCacheLevel(=maxLevel+1),所以这里满足level==maxCacheLevel条件的是obtainNewNodeLocalMapTask和obtainNewNodeOrRackLocalMapTask两个方法,即选择Node级别和TackNode级别的Map任务。而对于这两个任务是不需要进行下面两种方式选择Map任务的:推测执行任务和NoLocal任务,因为这两个方式选择的任务都不满足Node级别和TackNode级别,而是Any级别的,即也就只有obtainNewMapTask()这一个方法(其实还有一个方法obtainNewNonLocalMapTask(),传的maxCacheLevel参数值是NON_LOCAL_CACHE_LEVEL,即-1,这个方法会跳过注视中的1)方式)。下面继续看如何从根节点横向选择Map任务。

//2. Search breadth-wise across parents at max level for non-running
// TIP if
// - cache exists and there is a cache miss
// - node information for the tracker is missing (tracker's topology
// info not obtained yet) // collection of node at max level in the cache structure
Collection<Node> nodesAtMaxLevel = jobtracker.getNodesAtMaxLevel(); // get the node parent at max level
Node nodeParentAtMaxLevel =
(node == null) ? null : JobTracker.getParentNode(node, maxLevel - 1); for (Node parent : nodesAtMaxLevel) { // skip the parent that has already been scanned
if (parent == nodeParentAtMaxLevel) {
continue;
} List<TaskInProgress> cache = nonRunningMapCache.get(parent);
if (cache != null) {
tip = findTaskFromList(cache, tts, numUniqueHosts, false);
if (tip != null) {
// Add to the running cache
scheduleMap(tip); // remove the cache if empty
if (cache.size() == 0) {
nonRunningMapCache.remove(parent);
}
LOG.info("Choosing a non-local task " + tip.getTIPId());
return tip.getIdWithinJob();
}
}
}

这一种方式直接从根节点集合中选择任务,JT中nodesAtMaxLevel集合保存着所有没有父节点的Node信息,即在集群中处于最高级等的Node,下面就是直接遍历nodesAtMaxLevel中所有的节点选择满足条件的Map任务。同上一步也是从nonRunningMapCache集合中选择出对应Node上所有的未运行的Map任务,该方法基本同上一步一样,只是选择的Node不同。上一种方式是从TT所在的Node开始,自下而上选择Map任务,而此处则直接选择最高等级的Node上的Map任务。显然这一步不考虑任何的Map任务本地化因素。下面再看如何选择No-Local任务。

// 3. Search non-local tips for a new task
tip = findTaskFromList(nonLocalMaps, tts, numUniqueHosts, false);
if (tip != null) {
// Add to the running list
scheduleMap(tip); LOG.info("Choosing a non-local task " + tip.getTIPId());
return tip.getIdWithinJob();
}

这一种方式是从nonLocalMaps中选择一个Map任务,nonLocalMaps保存的任务是那些在任务初始化时未找到输入文件所在的Location信息的任务,这些任务是无法放到nonRunningMapCache中的。

以上三种方式其实都是一种方式——对应注释上的1)——选择未运行的任务,只是这里分成三种不同的选择方式:1)从TT所在节点自下而上选择满足Node和RackNode本地化要求的任务,2)直接从所有最高等级的Node上选择任务,3)选择那些输入文件没有Location信息的No-Local任务。下面接着看注释中提到的第三种选择方式——选择推测执行任务。

9.JobInProgress.findNewMapTask():

这一中方式同上面一种方式一样,也分为三个不同的选择方式(同上)。当然,这一中方法发生的条件是hasSpeculativeMaps==true,即该Job拥有推测执行任务,或者说可以启用推测执行任务,该参数由mapred.map.tasks.speculative.execution参数值决定,默认是true。下面分别看看三种方式。

// 1. Check bottom up for speculative tasks from the running cache
if (node != null) {
Node key = node;
for (int level = 0; level < maxLevel; ++level) {
Set<TaskInProgress> cacheForLevel = runningMapCache.get(key);
if (cacheForLevel != null) {
tip = findSpeculativeTask(cacheForLevel, tts,
avgProgress, currentTime, level == 0);
if (tip != null) {
if (cacheForLevel.size() == 0) {
runningMapCache.remove(key);
}
return tip.getIdWithinJob();
}
}
key = key.getParent();
}
}

第一种方式同上一种方式一样,依然是选择本地化Map任务(推测任务),不同的是这里是从runningMapCache中选择出Node上所有正在运行中的Task集合,从中选择一个Map任务对其启动推测执行任务。下面看看findSpeculativeTask()方法。

10.JobInProgress.findSpeculativeTask():

protected synchronized TaskInProgress findSpeculativeTask(
Collection<TaskInProgress> list, TaskTrackerStatus ttStatus,
double avgProgress, long currentTime, boolean shouldRemove) { Iterator<TaskInProgress> iter = list.iterator(); while (iter.hasNext()) {
TaskInProgress tip = iter.next();
// should never be true! (since we delete completed/failed tasks)
if (!tip.isRunning() || !tip.isRunnable()) {
iter.remove();
continue;
} if (tip.hasSpeculativeTask(currentTime, avgProgress)) {
// Check if this tip can be removed from the list.
// If the list is shared then we should not remove.
if(shouldRemove){
iter.remove();
}
if (!tip.hasRunOnMachine(ttStatus.getHost(),
ttStatus.getTrackerName())) {
return tip;
}
} else {
if (shouldRemove && tip.hasRunOnMachine(ttStatus.getHost(),
ttStatus.getTrackerName())) {
iter.remove();
}
}
}
return null;
}

选择依据是!tip.isRunning() || !tip.isRunnable(),即该Task处于运行中,且未完成,才能对此启动推测执行任务。

这里简单介绍下推测执行任务,推测执行任务是Hadoop的一种容错机制,即如果一个Task运行的时间同其他同类的Task所需的时间长很多(且还未完成)时,则根据实际情况考虑启动一个同样的Task,这时集群中就有两个同样的任务同时运行,哪个先完成则提交哪个Task,而kill掉另外一个Task。推测执行任务虽然能够能够更好的保证一个Task在正常时间内完成,但是代价是需要消耗更多的资源。

下面是调用hasSpeculativeTask()方法判断该Task是否可以启动一个推测执行任务。

boolean hasSpeculativeTask(long currentTime, double averageProgress) {
//
// REMIND - mjc - these constants should be examined
// in more depth eventually...
// if (!skipping && activeTasks.size() <= MAX_TASK_EXECS &&
(averageProgress - progress >= SPECULATIVE_GAP) &&
(currentTime - startTime >= SPECULATIVE_LAG)
&& completes == 0 && !isOnlyCommitPending()) {
return true;
}
return false;
}

判断条件有点多,主要是:1)skipping==false,即未开启跳过模式;2)该Task正在运行的任务数是否大于MAX_TASK_EXECS(1),大于MAX_TASK_EXECS则表示已经有一个推测执行的任务在运行了;3)averageProgress(Map任务或者Reduce任务完成的进度)是否大于SPECULATIVE_GAP(20%);4)任务运行的时间是否已超过SPECULATIVE_LAG(60*1000);5)Task的完成数==0;6)Task是否处于等待提交状态。

最后调用TaskInProgress的hasRunOnMachine()方法判断该Task是否在该TT上有正在运行中的TaskAttempt,且在该TT上是否有失败过。到这里findSpeculativeTask()方法就完成了。该方法首先根据Task的运行状态判断是否满足推测执行的条件,然后根据Task的一系列属性判断是否开启推测执行,最后根据该Task在该TT是否有正在运行的TaskAttempt以及是否有过失败记录最终决定是否在该TT上运行该Task的推测执行任务。继续回到JobInProgress.findNewMapTask()

10.JobInProgress.findNewMapTask():

// 2. Check breadth-wise for speculative tasks

      for (Node parent : nodesAtMaxLevel) {
// ignore the parent which is already scanned
if (parent == nodeParentAtMaxLevel) {
continue;
} Set<TaskInProgress> cache = runningMapCache.get(parent);
if (cache != null) {
tip = findSpeculativeTask(cache, tts, avgProgress,
currentTime, false);
if (tip != null) {
// remove empty cache entries
if (cache.size() == 0) {
runningMapCache.remove(parent);
}
LOG.info("Choosing a non-local task " + tip.getTIPId()
+ " for speculation");
return tip.getIdWithinJob();
}
}
} // 3. Check non-local tips for speculation
tip = findSpeculativeTask(nonLocalRunningMaps, tts, avgProgress,
currentTime, false);
if (tip != null) {
LOG.info("Choosing a non-local task " + tip.getTIPId()
+ " for speculation");
return tip.getIdWithinJob();
}
}

这里的两种方式其实跟上面一样,无需过多的解释。

到这里findNewMapTask()就完成了,下面回到obtainNewMapTaskCommon()方法。

11.JobInProgress.obtainNewMapTaskCommon():

int target = findNewMapTask(tts, clusterSize, numUniqueHosts, maxCacheLevel,
status.mapProgress());
if (target == -1) {
return null;
} Task result = maps[target].getTaskToRun(tts.getTrackerName());
if (result != null) {
addRunningTaskToTIP(maps[target], result.getTaskID(), tts, true);
// DO NOT reset for off-switch!
if (maxCacheLevel != NON_LOCAL_CACHE_LEVEL) {
resetSchedulingOpportunities();
}
}
return result;

当调用findNewMapTask()方法得到一个maps[]数组的索引之后,就可以从maps[]数组中获取对应的Map任务。这里调用了一个TaskInProgress的getTaskToRun()方法,为Task生成一个唯一的AttemptId,然后调用addRunningTask()方法创建一个Task对象,方法内部还是比较简单的,主要是new一个Task对象,并为其创建一个TaskStatus对象,以及初始化一些参数值。

如果Task!=null,则调用addRunningTaskToTIP()方法处理一些记录值,如记录Task的locality值,以及是否第一个TaskAttempt对象,等等。

到此obtainNewMapTaskCommon()方法就完成了,则obtainNewNodeLocalMapTask(),obtainNewNodeOrRackLocalMapTask(),obtainNewMapTask()三个方法也就都完成了。而obtainNewReduceTask()该方法基本和前面三个方法大同小异,也就不需要过多的解释,不同的只是Reduce任务不需要考虑本地性,选择相对更简单些。

以上就是一个Job如何选择一个Map/Reduce任务来执行的过程,总体上来看对于Map任务需要考虑Map任务的本地性,以提高执行效率。而任务的选择顺序依次是:失败的任务>未运行的任务>推测执行任务。而对于Map任务第二三种任务(未运行的任务>推测执行任务)又分成从TT所在的Node自下而上选择、从根节点横向选择、选择No-Local任务三种不同的方式。

OK,以上如有错误之处还望指出,谢谢!

FairScheduler的任务调度机制——assignTasks(续)的更多相关文章

  1. FairScheduler的任务调度机制——assignTasks

    首先需要了解FairScheduler是如何在各个Pool之间分配资源,以及每个Pool如何在Job之间分配资源的.FairScheduler的分配资源发生在update()方法中,而该方法由一个线程 ...

  2. Spark内核-任务调度机制

    作者:十一喵先森 链接:https://juejin.im/post/5e1c414fe51d451cad4111d1 来源:掘金 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. ...

  3. ucos-ii的任务调度机制

    1.在ucos-ii中,有这么几张表来管理任务. A.OSTCBPrioTbl[],其结构为OS_TCB指针的数组,其元素个数为64, 每一个元素对应一个任务的优先级,ucos-ii最多可以有64个任 ...

  4. spark内核篇-任务调度机制

    在生产环境中,spark 部署方式一般都是 yarn-cluster 模式,本文针对该模式进行讲解,当然大体思路也适用于其他模式 基础概念 一个 spark 应用包含 job.stage.task 三 ...

  5. rontab踩坑(三):crontab定时任务调度机制与系统时间/时区的不一致

    解决方案: 因为我们的服务器在是肯尼亚: 我么查看一下localtime 是否和 时区一致? 可以看到是一致的. 应该是是配置改动后未重启! service crond restart

  6. MapReduce多用户任务调度器——容量调度器(Capacity Scheduler)原理和源码研究

    前言:为了研究需要,将Capacity Scheduler和Fair Scheduler的原理和代码进行学习,用两篇文章作为记录.如有理解错误之处,欢迎批评指正. 容量调度器(Capacity Sch ...

  7. Quartz任务调度基本使用

    转自:http://www.cnblogs.com/bingoidea/archive/2009/08/05/1539656.html 上一篇:定时器的实现.Java定时器Timer和Quartz介绍 ...

  8. Spring之实现任务调度

    现实生活中,我们经常会制定一些"任务"在什么时间完成什么事情.同样在各种的企业中也会遇到这种任务调度的需求,比如商家或者网站的月报表 之类的要在每个月的最后一天统计各种数据,备份每 ...

  9. Spring任务调度

    任务调度是大多数应用系统的常见需求之一,拿论坛来说:每个半个小时生成精华文章的RSS文件,每天凌晨统计论坛用户的积分排名,每隔30分钟执行对锁定过期的用户进行解锁.以上都是以时间为关注点的调度,事实上 ...

随机推荐

  1. Checbox的操作含已选、未选及判断代码

    Checbox的操作包括已选.未选.判断等等,下面有个不错的示例,使用jquery完成,感兴趣的朋友可以参考下 $("#chk1").attr("checked" ...

  2. 2014年度辛星html教程夏季版第三节

    接下来我们继续学习HTML中的标签,希望大家能够再接再厉,同时辛星也会支持大家,我们一起努力,一起加油.我们本小节来认识另外几个标签. *************空格和换行************** ...

  3. Ajax编程相对路径与绝对路径

    http://www.worlduc.com/blog2012.aspx?bid=16946309 ajax同一域名调用采用相对路径 var url = 'QuerySingleDataByField ...

  4. 设计模式之观察者(Observer)模式 代码详解

    import java.util.ArrayList; import java.util.List; /** * User: HYY * Date: 13-10-28 * Time: 下午1:34 * ...

  5. [topcoder]BestRoads

    http://community.topcoder.com/stat?c=problem_statement&pm=10172&rd=13515 http://community.to ...

  6. Visual Studio 2015 Update 1 成功安装后运行 “出现未能正确加载[XXXX]包,此问题可能是由配置更改或安装另一个扩展导致的。” 可能的解决方法

    作死装Visual Studio 2015 update 1.安装过程中虽然波澜不惊,但是安装之后运行回报未能正确安装[XXXX]包.找了半天,在stackoverflow中找到了相关的问题,在问题描 ...

  7. android视频播放

    视频播放我们用到的是MediaPlayer,显示控件使用的surfaceView 我们向SD卡中先添加个视频文件,我的是xajh.3gp,不要用mp4,MP4会出现 should have subti ...

  8. [OJ] Find Minimum in Rotated Sorted Array II

    LintCode 160. Find Minimum in Rotated Sorted Array II (Medium) LeetCode 154. Find Minimum in Rotated ...

  9. redis key和value数据类型

    exists key del key1 key2 Redis 的vauleredis 提供五种数据类型:string,hash,list,set 及sorted set. string 是最基本的类型 ...

  10. Linux守护进程daemon

      守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启 ...