Hadoop1.2.1源码解析系列:JT与TT之间的心跳通信机制——JT篇

上一篇浅析了Hadoop心跳机制的TT(TaskTracker)方面,这一篇浅析下JT(JobTracker)方面。

我们知道心跳是TT通过RPC请求调用JT的heartbeat()方法的,TT在调用JT的heartbeat回收集自身的状态信息封装到TaskTrackerStatus对象中,传递给JT。下面看看JT如何处理来自TT的心跳。

1.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Make sure heartbeat is from a tasktracker allowed by the jobtracker.  
  2.    if (!acceptTaskTracker(status)) {  
  3.      throw new DisallowedTaskTrackerException(status);  
  4.    }  
第一步是检查发送心跳请求的TT是否属于可允许的TT,这个是根据一个HostsFileReader对象进行判断的,该对象是在实例化JT的时候创建的,这个类保存了两个队列,分别是includes和excludes队列,includes表示可以访问的host列表,excludes表示不可访问的host列表,这两个列表的内容根据两个mapred.hosts和mapred.hosts.exclude(mapred-site,xml中,默认是null)这两个参数指定的文件名读取的。具体可参考JT源码1956行。

2.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. String trackerName = status.getTrackerName();  
  2.     long now = clock.getTime();  
  3.     if (restarted) {  
  4.       faultyTrackers.markTrackerHealthy(status.getHost());  
  5.     } else {  
  6.       faultyTrackers.checkTrackerFaultTimeout(status.getHost(), now);  
  7.     }  
这一步是检查TT是否重启,是重启的话标识该TT的状态为健康的,否则检查TT的健康状态。faultyTrackers.markTrackerHealthy(status.getHost())内部将该TT所在的Host上所有的TT(从这里可以看出hadoop考虑到一个Host上可能存在多个TT的可能)从黑名单,灰名单和可能存在错误的列表上删除,也就是从potentiallyFaultyTrackers队列中移除该Host,通过更新JT的numGraylistedTrackers/numBlacklistedTrackers数量以及JT的totalMapTaskCapacity和totalReduceTaskCapacity数量。至于如何检查TT健康状态,具体是根据JT上记录的关于TT执行任务失败的次数来判断的(具体不是太理解)。

3.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. HeartbeatResponse prevHeartbeatResponse =  
  2.       trackerToHeartbeatResponseMap.get(trackerName);  
  3.     boolean addRestartInfo = false;  
  4.   
  5.     if (initialContact != true) {  
  6.       // If this isn't the 'initial contact' from the tasktracker,  
  7.       // there is something seriously wrong if the JobTracker has  
  8.       // no record of the 'previous heartbeat'; if so, ask the   
  9.       // tasktracker to re-initialize itself.  
  10.       if (prevHeartbeatResponse == null) {  
  11.         // This is the first heartbeat from the old tracker to the newly   
  12.         // started JobTracker  
  13.         if (hasRestarted()) {  
  14.           addRestartInfo = true;  
  15.           // inform the recovery manager about this tracker joining back  
  16.           recoveryManager.unMarkTracker(trackerName);  
  17.         } else {  
  18.           // Jobtracker might have restarted but no recovery is needed  
  19.           // otherwise this code should not be reached  
  20.           LOG.warn("Serious problem, cannot find record of 'previous' " +  
  21.                    "heartbeat for '" + trackerName +   
  22.                    "'; reinitializing the tasktracker");  
  23.           return new HeartbeatResponse(responseId,   
  24.               new TaskTrackerAction[] {new ReinitTrackerAction()});  
  25.         }  
  26.   
  27.       } else {  
  28.                   
  29.         // It is completely safe to not process a 'duplicate' heartbeat from a   
  30.         // {@link TaskTracker} since it resends the heartbeat when rpcs are   
  31.         // lost see {@link TaskTracker.transmitHeartbeat()};  
  32.         // acknowledge it by re-sending the previous response to let the   
  33.         // {@link TaskTracker} go forward.   
  34.         if (prevHeartbeatResponse.getResponseId() != responseId) {  
  35.           LOG.info("Ignoring 'duplicate' heartbeat from '" +   
  36.               trackerName + "'; resending the previous 'lost' response");  
  37.           return prevHeartbeatResponse;  
  38.         }  
  39.       }  
  40.     }  
此处第一句从JT记录的HeartbeatResponse队列中获取该TT的HeartbeatResponse信息,即判断JT之前是否收到过该TT的心跳请求。如果initialContact!=true,表示TT不是首次连接JT,同时如果prevHeartbeatResponse==null,根据注释可以知道如果TT不是首次连接JT,而且JT中并没有该TT之前的心跳请求信息,表明This is the first heartbeat from the old tracker to the newly started JobTracker。判断hasRestarted是否为true,hasRestarted是在JT初始化(initialize()方法)时,根据recoveryManager的shouldRecover来决定的,hasRestarted=shouldRecover,所以当需要进行job恢复时,addRestartInfo会被设置为true,即需要TT进行job恢复操作,同时从recoveryManager的recoveredTrackers队列中移除该TT。如果不需要进行任务恢复,则直接返回HeartbeatResponse,并对TT下重新初始化指令(后期介绍),注意此处返回的responseId还是原来的responseId,即responseId不变。上面说的都是prevHeartbeatResponse==null时的情况,下面说说prevHeartbeatResponse!=null时如何处理,当prevHeartbeatResponse!=null时会直接返回prevHeartbeatResponse,而忽略本次心跳请求。

4.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Process this heartbeat   
  2.     short newResponseId = (short)(responseId + 1);  
  3.     status.setLastSeen(now);  
  4.     if (!processHeartbeat(status, initialContact, now)) {  
  5.       if (prevHeartbeatResponse != null) {  
  6.         trackerToHeartbeatResponseMap.remove(trackerName);  
  7.       }  
  8.       return new HeartbeatResponse(newResponseId,   
  9.                    new TaskTrackerAction[] {new ReinitTrackerAction()});  
  10.     }  

首先将responseId+1,然后记录心跳发送时间。接着来看看processHeartbeat()方法。

5.JobTracker.processHeartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. boolean seenBefore = updateTaskTrackerStatus(trackerName,  
  2.                                                     trackerStatus);  
根据该TT的上一次心跳发送的状态信息更新JT的一些信息,如totalMaps,totalReduces,occupiedMapSlots,occupiedReduceSlots等,接着根据本次心跳发送的TT状态信息再次更新这些变量。

6.JobTracker.processHeartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. TaskTracker taskTracker = getTaskTracker(trackerName);  
  2.         if (initialContact) {  
  3.           // If it's first contact, then clear out   
  4.           // any state hanging around  
  5.           if (seenBefore) {  
  6.             lostTaskTracker(taskTracker);  
  7.           }  
  8.         } else {  
  9.           // If not first contact, there should be some record of the tracker  
  10.           if (!seenBefore) {  
  11.             LOG.warn("Status from unknown Tracker : " + trackerName);  
  12.             updateTaskTrackerStatus(trackerName, null);  
  13.             return false;  
  14.           }  
  15.         }  
如果该TT是首次连接JT,且存在oldStatus,则表明JT丢失了TT,具体意思应该是JT在一段时间内与TT失去了联系,之后TT恢复了,所以发送心跳时显示首次连接。lostTaskTracker(taskTracker):会将该TT从所有的队列中移除,并将该TT上记录的job清除掉(kill掉),当然对那些已经完成的Job不会进行次操作。当TT不是首次连接到JT,但是JT却没有该TT的历史status信息,则表示JT对该TT未知,所以重新更新TaskTracker状态信息。

7.JobTracker.processHeartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. updateTaskStatuses(trackerStatus);  
  2. updateNodeHealthStatus(trackerStatus, timeStamp);  
更新Task和NodeHealth信息,较复杂。

8.JobTracker.heartbeat():如果processHeartbeat()返回false,则返回HeartbeatResponse(),并下达重新初始化TT指令。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Initialize the response to be sent for the heartbeat  
  2.     HeartbeatResponse response = new HeartbeatResponse(newResponseId, null);  
  3.     List<TaskTrackerAction> actions = new ArrayList<TaskTrackerAction>();  
  4.     boolean isBlacklisted = faultyTrackers.isBlacklisted(status.getHost());  
  5.     // Check for new tasks to be executed on the tasktracker  
  6.     if (recoveryManager.shouldSchedule() && acceptNewTasks && !isBlacklisted) {  
  7.       TaskTrackerStatus taskTrackerStatus = getTaskTrackerStatus(trackerName);  
  8.       if (taskTrackerStatus == null) {  
  9.         LOG.warn("Unknown task tracker polling; ignoring: " + trackerName);  
  10.       } else {  
  11.         List<Task> tasks = getSetupAndCleanupTasks(taskTrackerStatus);  
  12.         if (tasks == null ) {  
  13.           tasks = taskScheduler.assignTasks(taskTrackers.get(trackerName));  
  14.         }  
  15.         if (tasks != null) {  
  16.           for (Task task : tasks) {  
  17.             expireLaunchingTasks.addNewTask(task.getTaskID());  
  18.             if(LOG.isDebugEnabled()) {  
  19.               LOG.debug(trackerName + " -> LaunchTask: " + task.getTaskID());  
  20.             }  
  21.             actions.add(new LaunchTaskAction(task));  
  22.           }  
  23.         }  
  24.       }  
  25.     }  
此处会实例化一个HeartbeatResponse对象,作为本次心跳的返回值,在初始化一个TaskTrackerAction队列,用于存放JT对TT下达的指令。首先需要判断recoveryManager的recoveredTrackers是否为空,即是否有需要回复的TT,然后根据TT心跳发送的acceptNewTasks值,即表明TT是否可接收新任务,并且该TT不在黑名单中,同上满足以上条件,则JT可以为TT分配任务。分配任务的选择方式是优先CleanipTask,然后是SetupTask,然后才是Map/Reduce Task。下面来看下getSetupAndCleanupTasks()方法。

9.JobTracker.getSetupAndCleanupTasks():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Don't assign *any* new task in safemode  
  2.     if (isInSafeMode()) {  
  3.       return null;  
  4.     }  
如果集群处于safe模式,则不分配任务。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int maxMapTasks = taskTracker.getMaxMapSlots();  
  2. int maxReduceTasks = taskTracker.getMaxReduceSlots();  
  3. int numMaps = taskTracker.countOccupiedMapSlots();  
  4. int numReduces = taskTracker.countOccupiedReduceSlots();  
  5. int numTaskTrackers = getClusterStatus().getTaskTrackers();  
  6. int numUniqueHosts = getNumberOfUniqueHosts();  
计算TT的最大map/reduce slot,以及已占用的map/reduce slot,以及集群可使用的TT数量,和集群的host数量。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. for (Iterator<JobInProgress> it = jobs.values().iterator();  
  2.              it.hasNext();) {  
  3.           JobInProgress job = it.next();  
  4.           t = job.obtainJobCleanupTask(taskTracker, numTaskTrackers,  
  5.                                     numUniqueHosts, true);  
  6.           if (t != null) {  
  7.             return Collections.singletonList(t);  
  8.           }  
  9.         }  
首先获取Job的Cleanup任务,每个Job有两个Cleanup任务,分别是map和reduce的。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. for (Iterator<JobInProgress> it = jobs.values().iterator();  
  2.              it.hasNext();) {  
  3.           JobInProgress job = it.next();  
  4.           t = job.obtainTaskCleanupTask(taskTracker, true);  
  5.           if (t != null) {  
  6.             return Collections.singletonList(t);  
  7.           }  
  8.         }  

然后获取一个Cleanup任务的TaskAttempt。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. for (Iterator<JobInProgress> it = jobs.values().iterator();  
  2.              it.hasNext();) {  
  3.           JobInProgress job = it.next();  
  4.           t = job.obtainJobSetupTask(taskTracker, numTaskTrackers,  
  5.                                   numUniqueHosts, true);  
  6.           if (t != null) {  
  7.             return Collections.singletonList(t);  
  8.           }  
  9.         }  
然后在获取Job的setup任务。上面这三个全部是获取的map任务,而下面是获取reduce任务,方法基本一样。

如果该方法返回null,则表示没有cleanup或者setup任务需要执行,则执行map/reduce任务。

10.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (tasks == null ) {  
  2.           tasks = taskScheduler.assignTasks(taskTrackers.get(trackerName));  
  3.         }  
此处是使用TaskScheduler调度任务,一大难点,后期分析。

11.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (tasks != null) {  
  2.           for (Task task : tasks) {  
  3.             expireLaunchingTasks.addNewTask(task.getTaskID());  
  4.             if(LOG.isDebugEnabled()) {  
  5.               LOG.debug(trackerName + " -> LaunchTask: " + task.getTaskID());  
  6.             }  
  7.             actions.add(new LaunchTaskAction(task));  
  8.           }  
  9.         }  
生成一个LaunchTaskAction指令。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // Check for tasks to be killed  
  2.     List<TaskTrackerAction> killTasksList = getTasksToKill(trackerName);  
  3.     if (killTasksList != null) {  
  4.       actions.addAll(killTasksList);  
  5.     }  
  6.        
  7.     // Check for jobs to be killed/cleanedup  
  8.     List<TaskTrackerAction> killJobsList = getJobsForCleanup(trackerName);  
  9.     if (killJobsList != null) {  
  10.       actions.addAll(killJobsList);  
  11.     }  
  12.   
  13.     // Check for tasks whose outputs can be saved  
  14.     List<TaskTrackerAction> commitTasksList = getTasksToSave(status);  
  15.     if (commitTasksList != null) {  
  16.       actions.addAll(commitTasksList);  
  17.     }  
以上分别是下达kill task指令,kill/cleanedup job指令,commit task指令。以上四种指令,加上一个ReinitTackerAction,这是心跳JT对TT下达的所有五种指令,以后可以相信对其进行分析。

12.JobTracker.heartbeat():

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // calculate next heartbeat interval and put in heartbeat response  
  2.     int nextInterval = getNextHeartbeatInterval();  
  3.     response.setHeartbeatInterval(nextInterval);  
  4.     response.setActions(  
  5.                         actions.toArray(new TaskTrackerAction[actions.size()]));  
  6.       
  7.     // check if the restart info is req  
  8.     if (addRestartInfo) {  
  9.       response.setRecoveredJobs(recoveryManager.getJobsToRecover());  
  10.     }  
  11.           
  12.     // Update the trackerToHeartbeatResponseMap  
  13.     trackerToHeartbeatResponseMap.put(trackerName, response);  
  14.   
  15.     // Done processing the hearbeat, now remove 'marked' tasks  
  16.     removeMarkedTasks(trackerName);  
剩下一些收尾工作,如计算下次发送心跳的时间,以及设置需要TT进行恢复的任务,更新trackerToHeartbeatResponseMap队列,移除标记的task。最后返回HeartbeatResponse对象,完成心跳请求响应。

到此JT的heartbeat()完成了,中间很多地方比较复杂,都没有去深追,以后有时间可以继续研究,如有错误,请不吝指教,谢谢

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值