在前面的博文中,我介绍了Job的调度以及Job的任务分解,但对于Job的调度我只是从宏观的角度作了详细的说明,而关于JobInProgress具体是如何给TaskTracker分配本地Map Task和非本地MapTask的,将是本文将要讲解的重点。
先来看看JobInProgress是如何定义本地Map Task的。在前面的博文:Job的任务分解中,我自己给出了一个关于“预分配map任务”的解释,在这个过程当中,待分配的map任务被放到了JobInProgress的两个集合中:
Map
List
而正在被TaskTracker的运行的map任务被存放在:
Map
List
显然地,本地map任务都来自于nonRunningMapCache集合,非本地map任务优先来自nonRunningMapCache,其次来自nonLocalMaps,最后也会从已经被执行的任务RunningMapCache、nonLocalRunningMaps中选取。下面就来看看JobInProgress是如何给TaskTracker分配本地map任务的吧!
在给TaskTracker分配一个本地map任务的过程中需要说明的有如下几点:
1.如何判断是否应该给当前TaskTracker分配一个map任务。
JobInProgress会记录每一执行它的任务的TaskTracker的信息,这个信息包括TaskTracker执行这个Job的tasks失败的次数。当这个次数超过一个阈值的时候,就任务不应该再给TaskTracker这个分配自己的tasks了,应该它执行自己的tasks的失败率太高。当然,这个阈值可以通过Job的配置文件来设置,对应的项是:mapred.max.tracker.failures。
2.如何检查TaskTracker当前的资源是否能够运行一个map任务
TaskTracker每次向JobTracker发送heartheat心跳包的时候会带上自己当前的资源信息(包括正在使用的,空闲的),返回时就会捎带JobTracker分配的tasks。比较TaskTracker的资源剩余量和一个任务的估计使用量就可以知道一个TaskTracker能够运行一个task。值得说明的是,这种判断不一定准确,一是因为TaskTracker的资源剩余量是动态变化的,当前获取的TaskTracker资源使用情况并不一定是最新的;而是因为并不能准确的估算出一个task到底需要多少资源。(这里提到的资源主要是指内存)
3.如何从集合cacheForLevel中选取一个map任务
1).从cacheForLevel中依次选取一个map任务;
2).如果这个map任务可执行并且没有正在被执行,到3);否则到6);
3).如果这个TaskTracker以前执行该map任务没有失败过或者集群中所有的主机执行该map任务都失败过,到4);否则到5);
4).从cacheForLevel中删除该map任务,并把该map任务分配给这个TaskTracker;到8);
5).如果该map任务对应的数据在TaskTracker上,则到6);否则到7);
6).从cacheForLevel中删除该map任务;
7).回到1);
8).结束。
再来看看JobInProgress是如何给TaskTracker分配非本地map任务的,虽然这个过程比较的繁琐,但是并不复杂。
关于从RunningMapCache、nonLocalRunningMaps中选取一个本地或者非本地的正在被执行的map任务的方法基本上类似于从nonRunningMapCache、nonLocalMaps中选取一个本地或者非本地的map任务的方法。不同的就是如何判断一个正在运行的task是speculative的。这个主要从这个正在执行的task的执行进度来衡量,说白了就是看看当前的Job中又没有拖后退的task。在实际的应用当中,总是会出现这样有趣的一个现象,那就是一个作业中总是有那么几个执行很慢的tasks,所以为了保证作业的整体执行进度,我们往往会把这些拖后退的tasks交给另外的TaskTrackers来执行,而这些拖后退的tasks在这些新的上执行很快TaskTrackers。从大的方面来讲,出现这种情况的原因很可能与操作系统的进程调度策略有关。
哦,对了,在向JobTracker提交作业Job的时候,客户端可以自己设置作业Job在执行的时候是否需要考虑处理这种拖后腿的task,即可以通过作业的配置文件中的mapred.map.tasks.speculative.execution项来设置(false/true),同时这个值默认是true。