接着上篇来说,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. 如何使用xshell远程连接ubuntu

    在Ubuntu上安装ssh就可以使用xshell登录了,安装步骤: 1,sudo apt-get install openssh-server 2,然后启动ssh sudo /etc/init.d/s ...

  2. Unity手撸2048小游戏——背景文字控制

    今天继续昨天的计划吧 1.新建项目.场景命名啥的都不说了吧. 2.直接开始新建一个Image,顺便把Image改名成Chessman 3.选中Image新建一个Text对象,调整下大小位置.这样就算完 ...

  3. Java开发中经典的小实例-(swich(){case:参数break;default: break;})

    import java.util.Scanner;public class Test6 {    public static void main(String[] args) {        // ...

  4. 运行 Spark on YARN

    运行 Spark on YARN Spark 0.6.0 以上的版本添加了在yarn上执行spark application的功能支持,并在之后的版本中持续的 改进.关于本文的内容是翻译官网的内容,大 ...

  5. 腾讯云>>云通信>>TLS后台API在mac上JAVA DEMO搭建

    1.相关文档地址 2.相关demo代码 代码部分作了修改,使用了commons-io中的IOUtils.toString简化了io操作 public class Demo { public stati ...

  6. fonts.googleapis.com 加载慢的解决方法

    把:fonts.googleapis.com 替换成 fonts.useso.com

  7. 1012. The Best Rank (25)

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  8. Spring jar包详解

    Spring jar包详解 org.springframework.aop ——Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework.asm——spri ...

  9. sublime 3 安装go环境

    安装go环境是在go已经安装的情况下, 1 首先安装 Package Control ctrl + · 打开sublime 命令行模式 复制粘贴以下代码 import urllib.request,o ...

  10. (转)Sqoop中文手册

    Sqoop中文手册 1.     概述 本文档主要对SQOOP的使用进行了说明,参考内容主要来自于Cloudera SQOOP的官方文档.为了用中文更清楚明白地描述各参数的使用含义,本文档几乎所有参数 ...