接着上篇来说,TaskTracker端的transmitHeartBeat()方法通过RPC调用JobTracker端的heartbeat()方法来接收心跳并返回心跳应答。还是先看看这张图,对它的大概流程有个了解。

  下面来一段一段的分析该方法。

 public synchronized HeartbeatResponse heartbeat(TaskTrackerStatus status,
boolean restarted,
boolean initialContact,
boolean acceptNewTasks,
short responseId)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug("Got heartbeat from: " + status.getTrackerName() + " (restarted: " + restarted +
" initialContact: " + initialContact +
" acceptNewTasks: " + acceptNewTasks + ")" +
" with responseId: " + responseId);
}
// 检查该主机是否允许与JobTracker通信
if (!acceptTaskTracker(status)) {
throw new DisallowedTaskTrackerException(status);
}

  上面这段代码首先检查该TaskTracker是否能与JobTracker通信。确保该TaskTracker在允许的主机列表里(即inHostsList(),由”mapred.hosts”指定),不在排除的主机列表里(即inExcludedHostsList(),由” mapred.hosts.exclude” 指定)。

  String trackerName = status.getTrackerName();
long now = clock.getTime();
if (restarted) {
faultyTrackers.markTrackerHealthy(status.getHost());
} else {
faultyTrackers.checkTrackerFaultTimeout(status.getHost(), now);
}

  上面这段代码表示:如果该TaskTracker被重启了,则将其标注为健康的,然后从黑名单和灰名单中移除,并从potentiallyFaultyTrackers(潜在有错误的Tracker。当有task运行失败时,就将其加入该队列中)集合中移除。否则,启动TaskTracker容错机制以检查它是否处于健康状态。

 HeartbeatResponse prevHeartbeatResponse =
trackerToHeartbeatResponseMap.get(trackerName); //获取上次心跳应答
boolean addRestartInfo = false; //表示JobTracker是否重启 if (initialContact != true) { //该TaskTracker不是初次连接JobTracker
// If this isn't the 'initial contact' from the tasktracker,
// there is something seriously wrong if the JobTracker has
// no record of the 'previous heartbeat'; if so, ask the
// tasktracker to re-initialize itself.
if (prevHeartbeatResponse == null) { //TaskTracker不是初次连接JobTracker,并且上一次心跳应答对象为空
// This is the first heartbeat from the old tracker to the newly
// started JobTracker
if (hasRestarted()) {  //JobTracker重启了
addRestartInfo = true;
// inform the recovery manager about this tracker joining back
recoveryManager.unMarkTracker(trackerName);
} else { //JobTracker没有重启
// Jobtracker might have restarted but no recovery is needed
// otherwise this code should not be reached
LOG.warn("Serious problem, cannot find record of 'previous' " +
"heartbeat for '" + trackerName +
"'; reinitializing the tasktracker");
return new HeartbeatResponse(responseId,
new TaskTrackerAction[] {new ReinitTrackerAction()}); //重新初始化该TaskTracker
} } else { //该TaskTracker不是第一次连接JobTracker,并且上次心跳应答不为空 // It is completely safe to not process a 'duplicate' heartbeat from a
// {@link TaskTracker} since it resends the heartbeat when rpcs are
// lost see {@link TaskTracker.transmitHeartbeat()};
// acknowledge it by re-sending the previous response to let the
// {@link TaskTracker} go forward.
if (prevHeartbeatResponse.getResponseId() != responseId) {
LOG.info("Ignoring 'duplicate' heartbeat from '" +
trackerName + "'; resending the previous 'lost' response");
return prevHeartbeatResponse; //心跳丢失,返回上次心跳应答
}
}
}

  上面这部分用来检测上次心跳是否结束。首先获取该TaskTracker的上次心跳应答响应。正常情况下在JobTracker中的trackerToHeartbeatResponseMap对象中会存在该TaskTracker上一次的心跳应答对象信息HeartbeatResponse,初次心跳连接则不会有该TaskTracker上一次的心跳应答对象。

  当该TaskTracker与JobTracker不是初次连接时:如果JobTracker中没有上次与该TaskTracker通信的心跳应答记录(即prevHeartbeatResponse == null),那么再检查JobTracker若重启了,则(来自该TaskTracker的心跳表明与JobTracker已经重新连接了)从recoveryManager中删除这个TaskTracker;否则重新初始化该TaskTracker。如果JobTracker有与TaskTracker通信的上次心跳记录(即prevHeartbeatResponse != null),但是JobTracker记录的心跳ID与TaskTracker发送过来的心跳ID不一致,说明发生了心跳丢失,此时返回上一次的心跳应答,这样可以防止处理重复的心跳请求。

    // Process this heartbeat
short newResponseId = (short)(responseId + 1);
status.setLastSeen(now);
if (!processHeartbeat(status, initialContact, now)) { //心跳处理失败
if (prevHeartbeatResponse != null) { //上次心跳应答不存在
trackerToHeartbeatResponseMap.remove(trackerName);
}
return new HeartbeatResponse(newResponseId,
new TaskTrackerAction[] {new ReinitTrackerAction()}); //重新初始化该TaskTracker
}

  这一部分来处理心跳信息。首先是心跳响应的ID加1,然后将心跳发送时间设置为当前时间。processHeartbeat()方法用来处理心跳。外层if表示心跳处理失败,内层if表示如果上次心跳应答存在的话,则从trackerToHeartbeatResponseMap中移除,然后重新初始化TaskTracker。最后分析processHeartbeat()方法。

  // Initialize the response to be sent for the heartbeat
HeartbeatResponse response = new HeartbeatResponse(newResponseId, null);
List<TaskTrackerAction> actions = new ArrayList<TaskTrackerAction>();
boolean isBlacklisted = faultyTrackers.isBlacklisted(status.getHost()); //检查该结点是否在黑名单中
// Check for new tasks to be executed on the tasktracker
if (recoveryManager.shouldSchedule() && acceptNewTasks && !isBlacklisted) {
TaskTrackerStatus taskTrackerStatus = getTaskTrackerStatus(trackerName);
if (taskTrackerStatus == null) {
LOG.warn("Unknown task tracker polling; ignoring: " + trackerName);
} else { //首先执行cleanup和setup task
List<Task> tasks = getSetupAndCleanupTasks(taskTrackerStatus);
if (tasks == null ) { //如果没有cleanup和setup task,则由作业调度器分配map和reduce task
tasks = taskScheduler.assignTasks(taskTrackers.get(trackerName));
}
if (tasks != null) {
for (Task task : tasks) {
expireLaunchingTasks.addNewTask(task.getTaskID());
if(LOG.isDebugEnabled()) {
LOG.debug(trackerName + " -> LaunchTask: " + task.getTaskID());
}
actions.add(new LaunchTaskAction(task));
}
}
}
}

  上面这部分主要用来构造心跳应答,其中包含对TaskTracker下达的命令。首先优先执行辅助型任务,其优先级为job-cleanup task、task-cleanup task(主要作用是清理失败的map或者reduce任务的部分结果数据)和job-setup task。它们由jobTracker直接调度,而且其调度的优先级比map和reduce任务都要高。从getSetupAndCleanupTasks()方法可以看出,执行时首先运行Map的辅助型任务,再执行Reduce的辅助型任务。然后将任务添加到expireLaunchingTasks队列中,监测其是否超时未汇报。最后为task构造LaunchTaskAction指令。如果没有辅助型任务,则由作业调度器TaskScheduler(默认为JobQueueTaskScheduler)来为TaskTracker分配任务。

  捎带分析下getSetupAndCleanupTasks()方法,顾名思义,这个方法是用来获得setup task和cleanup task的。在JobInProgress类的initTasks()方法中,我们看到有4类task,分别是setup、map、reduce、cleanup,而setup和cleanup是针对Job的,并没有task-cleanup的task,但在getSetupAndCleanupTasks()方法中却多出了这个task,这是为什么?原来在JobInProgress中维护了两个队列:mapCleanupTasks和reduceCleanupTasks,分别用来存放map和reduce需要清理的task。当task处于FAILED_UNCLEAN或KILLED_UNCLEAN状态时,则根据task的类型将其添加到对应的队列中。getSetupAndCleanupTasks()方法的执行过程为:先检查map的job-cleanup task(对应JobInProgress中的cleanup[0])、task-cleanup task和job-setup task(对应JobInProgress中的setup[0]),再检查reduce的job-cleanup task(对应JobInProgress中的cleanup[1])、task-cleanup task和job-setup task(对应JobInProgress中的setup[1])。其对应关系如下图所示:

    // Check for tasks to be killed
List<TaskTrackerAction> killTasksList = getTasksToKill(trackerName);
if (killTasksList != null) {
actions.addAll(killTasksList);
} // Check for jobs to be killed/cleanedup
List<TaskTrackerAction> killJobsList = getJobsForCleanup(trackerName);
if (killJobsList != null) {
actions.addAll(killJobsList);
} // Check for tasks whose outputs can be saved
List<TaskTrackerAction> commitTasksList = getTasksToSave(status);
if (commitTasksList != null) {
actions.addAll(commitTasksList);
}

  这三段代码分别构造KillTaskAction、KillJobAction和CommitTaskAction指令。

    // calculate next heartbeat interval and put in heartbeat response
int nextInterval = getNextHeartbeatInterval(); //计算下次心跳时间间隔
response.setHeartbeatInterval(nextInterval);
response.setActions( //将下达给TaskTracker的指令封装到心跳应答中
actions.toArray(new TaskTrackerAction[actions.size()])); // check if the restart info is req
if (addRestartInfo) { //将需要恢复的Job告知放入心跳应答中
response.setRecoveredJobs(recoveryManager.getJobsToRecover());
} // Update the trackerToHeartbeatResponseMap
trackerToHeartbeatResponseMap.put(trackerName, response); //将本次心跳应答更新到trackerToHeartbeatResponseMap,作为该TaskTracker最新的心跳应答 // Done processing the hearbeat, now remove 'marked' tasks
removeMarkedTasks(trackerName); //从trackerToMarkedTasksMap移除已被标记为完成的task return response;

  上面这段代码首先计算下次心跳间隔时间,JobTracker会根据集群规模(TaskTracker的数目)动态调整心跳时间间隔。如果JobTracker重启过的话,则将需要恢复的Job告知放入心跳应答中。然后将本次心跳应答更新到trackerToHeartbeatResponseMap,作为该TaskTracker最新的心跳应答。最后从trackerToMarkedTasksMap移除已被标记为完成的task。返回心跳应答。

  心跳时间间隔的计算方法如下所示:

   /**
* Calculates next heartbeat interval using cluster size.
* Heartbeat interval is incremented by 1 second for every 100 nodes by default.
* @return next heartbeat interval.
*/
public int getNextHeartbeatInterval() { //默认情况下,处理每个RPC大约要10ms
// get the no of task trackers
int clusterSize = getClusterStatus().getTaskTrackers(); //
int heartbeatInterval = Math.max(
(int)(1000 * HEARTBEATS_SCALING_FACTOR *
((double)clusterSize /
NUM_HEARTBEATS_IN_SECOND)),
HEARTBEAT_INTERVAL_MIN) ;
return heartbeatInterval;
}

  HEARTBEATS_SCALING_FACTOR(mapreduce.jobtracker.heartbeats.scaling.factor):规模因子,默认为1,最小是0.01

  NUM_HEARTBEATS_IN_SECOND(mapred.heartbeats.in.second):JobTracker每秒能处理的RPC数目,处理每个RPC约10ms时间。默认为100,最小为1。

  所以这个公式表示,当集群增加了mapred.heartbeats.in.second个结点,心跳间隔增加mapreduce.jobtracker.heartbeats.scaling.factor秒。但为了防止设置不合理而对JobTracker产生较大负载,JobTracker要求心跳间隔至少为300ms。

  最后分析下processHeartbeat()方法,还是先看图。

  processHeartbeat()方法用来处理心跳。首先会更新TaskTracker的状态资源信息(如task、slot数目,在updateTaskTrackerStatus()方法中实现),返回值seenBefore代表JobTracker上是否存在该TaskTracker的上次status信息。然后判断该TaskTracker与JobTracker如果是初次连接并且存在上次status信息,则将其清空;如果不是初次连接并且不存在上次status信息,则更新TaskTracker的资源状态信息并返回false,表示处理心跳出错。接着再次判断如果是初次连接并且该TaskTracker在黑名单中,则黑名单中host的数目加1;然后将该TaskTracker添加JobTracker中。最后更新该TaskTracker的Task信息和健康状态。

  本文基于hadoop1.2.1

  如有错误,还请指正

  参考文章:《Hadoop技术内幕 深入理解MapReduce架构设计与实现原理》 董西成

       http://zengzhaozheng.blog.51cto.com/8219051/1359887

       http://www.cnblogs.com/lxf20061900/p/3775963.html

  转载请注明出处:http://www.cnblogs.com/gwgyk/p/4055133.html

hadoop运行原理之Job运行(四) JobTracker端心跳机制分析的更多相关文章

  1. hadoop运行原理之Job运行(一) JobTracker启动及初始化

    这部分的计划是这样的,首先解释JobTracker的启动过程和作业从JobClient提交到JobTracker上:然后分析TaskTracker和heartbeat:最后将整个流程debug一遍来加 ...

  2. hadoop运行原理之Job运行(三) TaskTracker的启动及初始化

    与JobTracker一样,TaskTracker也有main()方法,然后以线程的方式启动(继承了Runnable接口).main()方法中主要包含两步:一是创建一个TaskTracker对象:二是 ...

  3. hadoop运行原理之Job运行(五) 任务调度

    接着上篇来说.hadoop首先调度辅助型task(job-cleanup task.task-cleanup task和job-setup task),这是由JobTracker来完成的:但对于计算型 ...

  4. hadoop运行原理之Job运行(二) Job提交及初始化

    本篇主要介绍Job从客户端提交到JobTracker及其被初始化的过程. 以WordCount为例,以前的程序都是通过JobClient.runJob()方法来提交Job,但是现在大多用Job.wai ...

  5. 【转载】Socket通讯原理以及TCP、IP三次握手机制分析

    要写网络程序就必须用Socket,这是程序员都知道的.而且,面试的时候,我们也会问对方会不会Socket编程?一般来说,很多人都会说,Socket编程基本就是listen,accept以及send,w ...

  6. Python+Appium运行简单的demo,你需要理解Appium运行原理!

    坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:oschina.云+社区.知乎等各大平台都有. 目录 一.Appium 的理念 四个原则 1.Web-Selenium 的运行原理 2.Appium ...

  7. html、JSP运行原理

    HTML运行原理 1.本地运行      所谓本地运行就是直接用 浏览器打开 2.远程访问的原理示意图: 何为协议?计算机互相通信(网络)的规则.常见的协议有 http .smtp. ftp.pop等 ...

  8. jmeter的运行原理和测试计划要素

    jmeter运行原理 1.jmeter运行在JVM虚拟机上,jmeter是以线程的方式运行的. 2.jmeter通过线程组来驱动多个线程,运行测试脚本对被测试服务器发起负载,每一个负载机上够可以运行多 ...

  9. ASP.Net中的四种状态保持机制

    每个人上网可多有过这样的情况,当我们登陆某个网站时,在登陆的旁边会有一个 "记住我" 的复选框,有的网站还会让用户选择记住我.这个记住我是怎么实现的呢? 其实就用利用的是cooki ...

随机推荐

  1. Spring+quartz整合问题

    今天一开始在弄quartz的时候用的2.0.2的jar包整合Spring3.0.5的时候报错 Java.lang.IncompatibleClassChangeError: class org.spr ...

  2. jquery了解

    jQuery 库 - 特性 jQuery 是一个 JavaScript 库. jQuery 是一个 JavaScript 函数库. jQuery 库包含以下特性: HTML 元素选取 HTML 元素操 ...

  3. LTE 测试文档(翻译)

    Testing Documentation 翻译 (如有不当的地方,欢迎指正!)     1 概述   为了测试和验证 ns-3 LTE 模块,文档提供了几个 test suites (集成在 ns- ...

  4. 《C++ Primer》学习笔记【第一部分 C++基础】

    第2章  整型的赋值:当我们试着把一个超出其范围的值赋给一个指定类型的对象时,结果如何?答案取决于类型是signed还是unsigned的.对于unsigned,编译器会将该值对unsigned类型的 ...

  5. ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存

    基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...

  6. cookie记录用户名

    在说如何用cookie记录用户名之前,我们先来说说cookie的工作原理: cookie : 存储数据,当用户访问了某个网站(网页)的时候,我们就可以通过cookie来像访问者电脑上存储数据 ; 1. ...

  7. spring cloud资料

    https://segmentfault.com/a/1190000006216281 http://git.oschina.net/zhou666/spring-cloud-7simple zuul ...

  8. easyui combobox 左匹配模糊查询

    之前一直不知道,easyui 的combobox还有从左匹配查询显示数据的. 样式是这样的:(这是数据是已经存在下拉列表里的) 在这样操作的时候,遇到了一个问题.(其实也不算问题的). 就是操作人员在 ...

  9. ROS学习笔记(一)——软件版本的选择

    下面是Google的SLAM系统Cartographer对系统的要求: Cartographer对ROS版本要求: ROS Indigo 对Ubantu 的版本要求: 所以,综上所述: Ubantu ...

  10. vertical-align

    http://www.zhangxinxu.com/wordpress/2015/08/css-deep-understand-vertical-align-and-line-height/