Hadoop的默认调度器JobQueueTaskScheduler的一个Map Task Pending问题

    在正式环境的Hadoop任务调度里,集群整体资源还有大量slot的情况下,出现了某些MapReduce任务无法被调度,一直处于pending状态,无法获得集群slot资源进行计算的诡异情况。更改几个指定类的loglevel后,查看其Debug的log发现了问题原因:这是由于Hadoop 1.x默认的FIFO调度器JobQueueTaskScheduler在进行集群均衡计算时,忽略Speculative Task任务数的计算,导致计算后的每个节点上可以调度的slot为0(实际上还有大量空闲slot)。

        经过把默认调度器替换为CapacityScheduler后,由于CapacityScheduler不考虑集群均衡问题,直接计算出每个节点空闲slot,问题得以解决。

  • 具体分析

(1)JobQueueTaskScheduler的调度机制

 

    

        如上图所示,集群里所有的TaskTracker每隔固定时间通过Heartbeat包和JobTracker通信,同时告诉JobTracker自己运行Map/Reduce Task的数量和进度等信息。当JobTracker接收到TaskTracker的Heartbeat后,把相关信息传递到TaskScheduler(这里为默认的JobQueueTaskScheduler),然后TaskScheduler根据一定的算法,把之前客户端提交给JobTracker后,保存在TaskScheduler任务队列JobQueue的Task分配给当前的TaskTracker执行。然后JobTracker通过Heartbeat响应,通知相应的TaskTracker执行Map/Reduce Task。

        JobTracker的默认任务调度器JobQueueTaskScheduler调度Map Task算法大致如下:

//remainingMapLoad是JobQueue里所有没被调度运行和正在运行的Map数。
int remainingMapLoad = jobQueue.desiredMaps() - jobQueue.finishedMaps();
//clusterMapCapacity是集群里所有节点map slot数
double mapLoadFactor = remainingMapLoad / clusterMapCapacity;
//trackerMapCapacity是当前TaskTracker的最大Map slot数
finalint trackerCurrentMapCapacity = Math.min((int)Math.ceil(mapLoadFactor * trackerMapCapacity), trackerMapCapacity);
//trackerRunningMaps是当前TaskTracker已经运行的Map task数
int availableMapSlots = trackerCurrentMapCapacity - trackerRunningMaps;

        所得出的availableMapSlots 就是当前节点可以分配的Map slot数。可见,JobQueueTaskScheduler考虑到当前任务队列里的Task数,以及当前集群的负载,进行一个负载均衡的算法,得出当前每个节点实际要分配的Slot数,然后按优先级分配给JobQueue里的Job。Reduce Task分配类似,此处不再赘述。

(2)Map Task Pending问题

        集群的正式环境总共有12个TaskTracker节点,每个节点可以分配的Map Task Slot数为11。这时,Hive上提交了一个查询PHP脚本出现异常,导致运行缓慢的的MR任务。该MR任务初始化为8个Map Task的Job。根据之前的JobQueueTaskScheduler调度算法,我们可以计算出每个TaskTracker可以分配的Map slot数availableMapSlots :

remainingMapLoad = 8;
trackerMapCapacity = 11;
clusterMapCapacity = 12 × 11 = 132;
mapLoadFactor = 8 / 132 = 0.0606;
trackerCurrentMapCapacity = 1;
availableMapSlots = 1 - trackerRunningMaps;

        也就是经过集群负载均衡后,每个节点最多只有1个可分配的Map Slot,如果已经有Map Task运行在上面,则无法继续分配Slot。

        在运行过程中,有部分Map任务在进度0%时会卡住很长一段时间,另外也有部分任务会先运行到30%左右的进度,然后一直卡死,这时由于Hadoop的Speculative Task机制(即对于某个task在60s内落后于任务平均进度20%,会启用另外一个attemp task来执行任务),对0%卡住的Map任务启用了另外的attemp task。

        这时,在集群的每个节点里,事实上都已经运行了最起码1个Map Task。调整loglevel后,查看JobTracker的log可以看到如下的内容(经过简略):

……
2014-12-30 17:20:53,626 DEBUG org.apache.hadoop.mapred.JobTracker: Got heartbeat from: tracker_datanode5.dataplat.com:localhost/127.0.0.1:25999 (restarted: false initialContact: false acceptNewTasks: true) with responseId: 11702
2014-12-30 17:20:53,626 DEBUG org.apache.hadoop.mapred.JobTracker: tracker_datanode5.dataplat.com:localhost/127.0.0.1:25999: Status - running(m) = 1 unassigned(m) = 0 commit_pending(m) = 0 misc(m) = 0 running(r) = 0 unassigned(r) = 0 commit_pending(r) = 0 misc(r) = 0
2014-12-30 17:20:53,626 DEBUG org.apache.hadoop.mapred.JobInProgress: Taking progress for task_201412111518_52173_m_000004 from 0.0 to 0.0
2014-12-30 17:20:53,626 DEBUG org.apache.hadoop.mapred.JobQueueTaskScheduler: Task assignments for tracker_datanode5.dataplat.com:localhost/127.0.0.1:25999 --> [0.07575757575757576, 11, 1, 1] -> [0, 0 (0, 0)] [0.016666666666666666, 5, 1,0] -> [1, 0]
2014-12-30 17:20:53,664 DEBUG org.apache.hadoop.mapred.JobTracker: Got heartbeat from: tracker_datanode3.dataplat.com:localhost/127.0.0.1:42298 (restarted: false initialContact: false acceptNewTasks: true) with responseId: 10488
……

        我们注意到JobTracker接收到来自datanode5的heartbeat包,并且看到id为task_201412111518_52173_m_000004的Map Task的进度维持0.0没有改变,JobQueueTaskScheduler打印的log内容[0.07575757575757576, 11, 1, 1],第一个浮点数为mapLoadFactor,11为trackerMapCapacity,第一个1为trackerCurrentMapCapacity,第2个1为 trackerRunningMaps,因此availableMapSlots为0。

在很长一段时间内,从datanode1-datanode12,它们的availableMapSlots都为0,也就是当前无法给后续任务进行任何调度,尽管此时集群上仅仅使用了大约12个slot,仍然有100多个slot没法被调度。

当我们继续提交一个只有2个Map Task的小型MR任务时,重新计算每个TaskTracker的trackerCurrentMapCapacity,也就是最大可分配的Map槽数,依然为1个,此时由于之前的Map Task同样卡住,所有这个小型MR任务没有办法进行调度获得任何Slot。

(3)问题解决分析

        出现以上问题,其实很大原因是由于JobQueueTaskScheduler的调度算法里,没有考虑到Speculative Task机制而使集群多运行Map Task,从而导致集群负载均衡算法不准确。

        一个直接的解决方案是,修改调度算法,重新添加上Speculative Task多出任务数。但这个可能涉及一定的逻辑修改,而且这些模块之间的调度逻辑还是有一定的复杂性,有一定的风险。

        而且,事实上,由于MapReduce的设计本身就是希望把具体的Task放到具体数据存储的节点上,尽可能保证data-local可以减少网络IO,最大化任务执行效率。在这个过程中,很难避免某几个节点的负载较大的可能性,此时集群负载均衡的优先级就没有那么重要。

       另外的一个解决方案是,替换TaskScheduler。首先考虑到的是CapacityScheduler。经过阅读源代码,CapacityScheduler在调度Task的时候只是简单的把TaskTracker最大Map Slot数减去已经被占用的Map Task数,即为当前TaskTracker可分配的Map Slot数,并没有考虑集群负载均衡的问题。

        另外尽管CapacityScheduler提供了多用户多队列的优先级FIFO调度机制,但默认配置就是单队列单用户的FIFO调度机制,因此不需要大量的参数配置考虑,只需要简单指定CapacityTaskScheduler类即可。另外默认配置下,CapacityScheduler是把集群里所有的Slot分配的当前的所有队列里,当只有单队列时,CapacityScheduler就会把集群所有的Map Slot分配到这个队列,不会有任何保留和考虑因素。

        CapacityTaskScheduler同时也是Hadoop 2.x默认的任务调度器,可以提前应用,方便后面升级习惯。

        经过分析考虑,决定采用替换CapacityTaskScheduler的解决方案。

  • 方案验证

        其实方案验证很简单,只要写了一个简单的模拟之前Map Task Pending的环境,然后继续提交一个小型MR任务,看是否卡死即可。

        验证的MapReducer任务算法比较简单。具体大致思维是希望有部分Map Task会首先运行到一定进度(起码超过20%),然后让另外一部分Map Task在启动时候卡死,这个我们可以利用Map类在创建的时候的随机数来决定一个阀值,然后超过此阀值则Sleep指定时间实现。然后当Map任务运行到一定快结束的时候,可以Sleep更长的时间减慢进度,腾出时间让后面的小型MR任务顺利启动,以便查看结果。

        最后,经过几次试验,我们发现CapacityTaskScheduler能够顺利解决Map Task Pending问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值