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

 public static void main(String argv[]) throws Exception {
...
try {
JobConf conf=new JobConf();
...
TaskTracker tt = new TaskTracker(conf);
...
tt.run();
} catch (Throwable e) {
...
}
}

  一、TaskTracker tt = new TaskTracker(conf);  该方法会首先获取Map和Reduce的slot数量、磁盘检查时间间隔和JobTracker的地址;然后构造一个HttpServer(Jetty服务器)对象,这样可以通过web查看该tasktracker的运行情况及所有task;接着构造TaskController对象,用来控制任务的初始化、终结和清理工作;再接着调用initialize()方法来初始化一些变量并构造TaskTracker对象;最后启动HttpServe服务器。

  着重说下initialize()方法。这个方法真正完成了构造TaskTracker对象的工作,并初始化了一些变量。用这个单独的方法来构造TaskTracker,可以使我们多次调用它。首先initialize()方法会清理一些历史文件(第一次启动TaskTracker不会有历史文件),并新建一些目录;接着初始化一些变量;构造JvmManager对象用来管理JVM;初始化并启动taskReportServer,Task通过RPC向该Server汇报进度;初始化distributedCacheManager对象用来管理分布式缓存;初始化InterTrackerProtocol对象,TaskTracker由此与JobTracker通信;构造两个TaskLauncher线程:mapLauncher和reduceLauncher并启动,这个线程的run函数就是不断监测taskToLaunch队列中是否有新的 TaskTracker.TaskInProgress对象加入。如果有则从中取出一个对象,然后调用TaskTracker类的 startNewTask(TaskInProgress tip)来启动一个task。

  二、tt.run();  该方法维护一个与JobTracker的通信连接,以周期性地向JobTracker发送心跳并领取新任务。

 public void run() {
try {
getUserLogManager().start();
startCleanupThreads();
boolean denied = false;
while (running && !shuttingDown) {
boolean staleState = false;
try {
// This while-loop attempts reconnects if we get network errors
while (running && !staleState && !shuttingDown && !denied) { //该循环条件表示与JobTracker连接成功;
//否则的话会在外层循环中一直尝试执行该内层循环
try {
//如果连接JobTracker服务成功,TaskTracker就会调用offerService()函数进入主执行循环中。
//offerService()中的循环会每隔一段时间与JobTracker通讯一次,调用transmitHeartBeat(),获得HeartbeatResponse信息。
State osState = offerService();
if (osState == State.STALE) {
staleState = true;
} else if (osState == State.DENIED) {
denied = true;
}
} catch (Exception ex) {
if (!shuttingDown) {
LOG.info("Lost connection to JobTracker [" +
jobTrackAddr + "]. Retrying...", ex);
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
}
}
}
} finally {
// If denied we'll close via shutdown below. We should close
// here even if shuttingDown as shuttingDown can be set even
// if shutdown is not called.
if (!denied) {
close();
}
}
if (shuttingDown) { return; }
if (denied) { break; }
LOG.warn("Reinitializing local state");
initialize();
}
if (denied) {
shutdown(); //关闭该TaskTracker
}
} catch (IOException iex) {
LOG.error("Got fatal exception while reinitializing TaskTracker: " +
StringUtils.stringifyException(iex));
return;
} catch (InterruptedException i) {
LOG.error("Got interrupted while reinitializing TaskTracker: " +
i.getMessage());
return;
}
}

  该方法主体是两层while循环。如果与JobTracker的连接一旦断开,则反复尝试运行内层循环,直到与JobTracker成功连接为止;连接成功后会调用内层循环中的offerService()方法与JobTracker进行通信。

 State offerService() throws Exception {
long lastHeartbeat = System.currentTimeMillis(); //上次心跳时间 while (running && !shuttingDown) {
try {
long now = System.currentTimeMillis(); // accelerate to account for multiple finished tasks up-front
//计算上次心跳到现在的时间隔
//通过完成的任务数动态调整时间间隔
long remaining =
(lastHeartbeat + getHeartbeatInterval(finishedCount.get())) - now; if (remaining <= 0) { //达到心跳时间间隔
finishedCount.set(0);
} while (remaining > 0) { //未达到心跳时间间隔
// sleeps for the wait time or
// until there are *enough* empty slots to schedule tasks
synchronized (finishedCount) {
finishedCount.wait(remaining); //wait相差的时间(remaining) // Recompute
now = System.currentTimeMillis();
remaining =
(lastHeartbeat + getHeartbeatInterval(finishedCount.get())) - now; if (remaining <= 0) {
// Reset count
finishedCount.set(0);
break;
}
}
} // If the TaskTracker is just starting up:
// 1. Verify the versions matches with the JobTracker
// 2. Get the system directory & filesystem
if(justInited) {
String jtBuildVersion = jobClient.getBuildVersion();
String jtVersion = jobClient.getVIVersion();
if (!isPermittedVersion(jtBuildVersion, jtVersion)) {
String msg = "Shutting down. Incompatible version or build version." +
"TaskTracker version '" + VersionInfo.getVersion() +
"' and build '" + VersionInfo.getBuildVersion() +
"' and JobTracker version '" + jtVersion +
"' and build '" + jtBuildVersion +
" and " + CommonConfigurationKeys.HADOOP_RELAXED_VERSION_CHECK_KEY +
" is " + (relaxedVersionCheck ? "enabled" : "not enabled") +
" and " + CommonConfigurationKeys.HADOOP_SKIP_VERSION_CHECK_KEY +
" is " + (skipVersionCheck ? "enabled" : "not enabled");
LOG.fatal(msg);
try {
jobClient.reportTaskTrackerError(taskTrackerName, null, msg);
} catch(Exception e ) {
LOG.info("Problem reporting to jobtracker: " + e);
}
return State.DENIED;
} String dir = jobClient.getSystemDir();
while (dir == null) {
LOG.info("Failed to get system directory..."); // Re-try
try {
// Sleep interval: 1000 ms - 5000 ms
int sleepInterval = 1000 + r.nextInt(4000);
Thread.sleep(sleepInterval);
} catch (InterruptedException ie)
{}
dir = jobClient.getSystemDir();
}
systemDirectory = new Path(dir);
systemFS = systemDirectory.getFileSystem(fConf);
} now = System.currentTimeMillis();
if (now > (lastCheckDirsTime + diskHealthCheckInterval)) { //是否达到磁盘检查间隔时间
localStorage.checkDirs();
lastCheckDirsTime = now;
int numFailures = localStorage.numFailures();
// Re-init the task tracker if there were any new failures
if (numFailures > lastNumFailures) {
lastNumFailures = numFailures;
return State.STALE;
}
} // Send the heartbeat and process the jobtracker's directives
//发送Heartbeat到JobTracker,得到response
HeartbeatResponse heartbeatResponse = transmitHeartBeat(now); // Note the time when the heartbeat returned, use this to decide when to send the
// next heartbeat
lastHeartbeat = System.currentTimeMillis(); // Check if the map-event list needs purging
Set<JobID> jobs = heartbeatResponse.getRecoveredJobs();
if (jobs.size() > 0) {
synchronized (this) {
// purge the local map events list
for (JobID job : jobs) {
RunningJob rjob;
synchronized (runningJobs) {
rjob = runningJobs.get(job);
if (rjob != null) {
synchronized (rjob) {
FetchStatus f = rjob.getFetchStatus();
if (f != null) {
f.reset();
}
}
}
}
} // Mark the reducers in shuffle for rollback
synchronized (shouldReset) {
for (Map.Entry<TaskAttemptID, TaskInProgress> entry
: runningTasks.entrySet()) {
if (entry.getValue().getStatus().getPhase() == Phase.SHUFFLE) {
this.shouldReset.add(entry.getKey());
}
}
}
}
} //调用HeartbeatResponse的getActions()函数获得JobTracker传过来的所有指令,即一个TaskTrackerAction数组
TaskTrackerAction[] actions = heartbeatResponse.getActions();
if(LOG.isDebugEnabled()) {
LOG.debug("Got heartbeatResponse from JobTracker with responseId: " +
heartbeatResponse.getResponseId() + " and " +
((actions != null) ? actions.length : 0) + " actions");
}
if (reinitTaskTracker(actions)) {
return State.STALE;
} // resetting heartbeat interval from the response.
heartbeatInterval = heartbeatResponse.getHeartbeatInterval();
justStarted = false;
justInited = false;
if (actions != null){
//遍历这个数组
for(TaskTrackerAction action: actions) {
if (action instanceof LaunchTaskAction) { //如果是LaunchTaskAction,则调用调用addToTaskQueue加入到待执行队列
addToTaskQueue((LaunchTaskAction)action);
} else if (action instanceof CommitTaskAction) { //如果是CommitTaskAction,则添加到需要提交的队列中
CommitTaskAction commitAction = (CommitTaskAction)action;
if (!commitResponses.contains(commitAction.getTaskID())) {
LOG.info("Received commit task action for " +
commitAction.getTaskID());
commitResponses.add(commitAction.getTaskID());
}
} else { //如果是KillJobAction或者KillTaskAction,则加入到tasksToCleanup队列,交给一个taskCleanupThread线程来处理
addActionToCleanup(action);
}
}
}
//杀死一定时间没没有汇报进度的task
markUnresponsiveTasks(); //当剩余磁盘空间小于mapred.local.dir.minspacekill(默认为0)时,寻找合适的任务将其杀掉以释放空间
killOverflowingTasks(); //we've cleaned up, resume normal operation
if (!acceptNewTasks && isIdle()) {
acceptNewTasks=true;
}
//The check below may not be required every iteration but we are
//erring on the side of caution here. We have seen many cases where
//the call to jetty's getLocalPort() returns different values at
//different times. Being a real paranoid here.
checkJettyPort(server.getPort());
} catch (InterruptedException ie) {
LOG.info("Interrupted. Closing down.");
return State.INTERRUPTED;
} catch (DiskErrorException de) {
String msg = "Exiting task tracker for disk error:\n" +
StringUtils.stringifyException(de);
LOG.error(msg);
synchronized (this) {
jobClient.reportTaskTrackerError(taskTrackerName,
"DiskErrorException", msg);
}
// If we caught a DEE here we have no good dirs, therefore shutdown.
return State.DENIED;
} catch (RemoteException re) {
String reClass = re.getClassName();
if (DisallowedTaskTrackerException.class.getName().equals(reClass)) {
LOG.info("Tasktracker disallowed by JobTracker.");
return State.DENIED;
}
} catch (Exception except) {
String msg = "Caught exception: " +
StringUtils.stringifyException(except);
LOG.error(msg);
}
} return State.NORMAL;
}

  通过下面这个图可以很清楚的了解这个方法的功能:

  该方法的主体是一个while循环,首先检查是否达到心跳间隔时间,如果没有的话则阻塞一定时间(wait);然后检查TaskTracker是否刚启动,如果是的话需要先检查JobTracker版本和TaskTracker版本是否一致,不一致的话返回State.DENIED;再获取system directory和filesystem;接着如果达到磁盘检查时间,则检查磁盘是否有损坏;再接着transmitHeartBeat(now)方法将当前结点的运行时信息(如资源使用情况、任务运行状态等),通过心跳汇报给JobTracker,同时接收来自JobTracker的各种命令;JobTracker将心跳应答封装在HeartbeatResponse对象中,该对象包括两部分内容:第一是作业集合recoveredJobs,它是上次关闭JobTracker时正在运行的作业集合,重启JobTracker后需要恢复这些作业的运行状态;第二是需要执行的命令列表actions。接着会检查这些命令里是否包括了重新初始化命令(ReinitTrackerAction),如果有则返回State.STALE,会重新初始化TaskTracker;修正心跳间隔;遍历actions数组,如果是LaunchTaskAction,则根据是MapTask还是ReduceTask,分别放进相应的mapLauncher和reduceLauncher中,这两个launcher都是TaskLauncher;如果是CommitTaskAction则加入到commitResponses;其他的类型放入tasksToCleanup.put(action),表示要清理,如执行KillJobAction或者KillTaskAction等。kUnresponsiveTasks()方法杀死一定时间内没有汇报进度的任务;killOverflowingTasks()中,当剩余磁盘空间小于mapred.local.dir.minspacekill(默认为0)时,寻找合适的任务将其杀掉以释放空间(优先杀掉Reduce任务)。

  下面分析下transmitHeartBeat(now)方法,这是offerService()方法中最重要的步骤。还是先来看个图,这样能对这个方法的流程有个大概了解。

  transmitHeartBeat(now)方法主要是构造心跳信息(会把当前结点信息和该结点上的所有task信息告知JobTracker)并向JobTracker发送心跳的。首先它会检查是否达到计数器时间间隔(60s),如果没有达到的话,那么就不发送任务的计数器(Counter)信息,否则要发送。然后检查上次心跳是否结束,如果没有结束,则继续使用上次的结点状态信息;如果结束的话则构造新的结点状态信息(会把当前结点的健康状态、资源情况以及task信息封装到里边)。接着判断该TaskTracker是否能够接收新任务,如果能够接收新任务,则将当前结点的起源情况设置到status对象里。接着再设置结点的健康状态。结点的健康状态由NodeHealthCheckerService线程来监测,该线程运行一个脚本来监控结点的运行情况;如果该线程监测到脚本输出里有“ERROR”开头行,则将结点标记为“unhealthy”;如果没有设置该脚本时,则不启动该线程,结点默认“healthy”;最后通过RPC调用JobTracker的heartbeat()方法。在得到心跳响应后,将status标记为null,表示本次心跳通信结束,并返回心跳响应。

  关于TaskTracker的一个思考。TaskTracker与JobTracker不同的是,TaskTracker是以线程的方式启动的,而JobTracker却不是。这是否也说明了Hadoop采用的pull的模式,从这儿看出TaskTracker主动与JobTracker进行通信?

  本文基于hadoop1.2.1

  如有错误,还请指正

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

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

hadoop运行原理之Job运行(三) TaskTracker的启动及初始化的更多相关文章

  1. hadoop运行原理之Job运行(四) JobTracker端心跳机制分析

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

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

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

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

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

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

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

  5. RocketMQ详解(三)启动运行原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  6. ADB 运行原理

    ADB基本命令和简介 ADB就是Android Debug Bridge,Android调试桥的意思,很形象.需要在电脑上安装SDK Platform Tools 对应的版本才能使用 基于ADB的工具 ...

  7. Dubbo(一):Dubbo运行原理

    前言: 在开始入门Javaweb时,学的基本都是MVC开发模式,一个项目基本上就是model,view,controller三层.但是随着系统的服务逐渐加多,SOA模式更加适合目前项目开发.而SOA模 ...

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

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

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

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

随机推荐

  1. busybox-1.12.2编译提示“混合的隐含和普通规则”错误解决

    编译环境:CentOs 7.1 Ubuntu 12.0.4 都可以 交叉编译工具:gcc -4.3.2 (博创6410平台) 问题描述:执行make menuconfig命令的时候,提示Makefil ...

  2. mysql数据库的主从

    1. Slave 上面的IO线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容; 2. Master 接收到来自 Slave 的 IO 线程的请求后,通过负责 ...

  3. 在C#中??和?分别是什么意思?

    在C# 6.0中,引入了一个 ?. 的运算符,前面的代码可以改成如下形式: int? firstX = points?.FirstOrDefault()?.X; 从这个例子中我们也可以看出它的基本用法 ...

  4. SSH 无密码远程执行脚本

    ssh无密码登录及远程执行脚本要使用公钥与私钥.linux下可以用用ssh-keygen生成公钥/私钥对,下面我以CentOS7为例. 测试环境:机器A(10.0.224.80):机器B(192.16 ...

  5. WPF:设置MenuItem多种不同状态图标

    需求描述: 给MenuItem内部的子Image设置默认图标(鼠标leave).鼠标hover图标.和选中时的图标. 注:是给Menu内个别MenuItem修改,并且是弹出子菜单. 问题描述: 1)前 ...

  6. eclipse 设置jsp页面为HTML5

    window-preferences-web-jspFiles-Editor-Templates-jsp with html... 然后修改为<!DOCTYPE html>就行了

  7. html里面的相对路径和绝对路径

    HTML有两种路径的写法:相对路径和绝对路径. 相对路径是在同一个目录的文件引用,如果源文件和引用文件在同一个目录里,直接写引用文件名即可. 如: a.html:     c:\test\menu\a ...

  8. hash命令

    什么是hash ? 在网上找了好久都没找到简截有力的说明.hash 我把它当成是集合,一个hash 就是一个集合,里面字段对应一个元素,元素不重复,字段都不一样. 简单hash 命令 1.hset 哈 ...

  9. ASP值view State

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  10. 深入理解为什么Java中方法内定义的内部类可以访问方法中的局部变量

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/19996629 开篇 在我的上一篇博客 深入理解Java中为什么内部类可以访问外部类的成 ...