[Hadoop] - TaskTracker源码分析
在Hadoop1.x版本中,MapReduce采用master/salve架构,TaskTracker就是这个架构中的slave部分。TaskTracker以服务组件的形式存在,负责任务的执行和任务状态的汇报。TaskTracker是hadoop集群中运行在各个节点上的服务。扮演着“通信交通”的角色,是JobTracker和Task之间的“沟通桥梁”,一方面,TaskTracker发送心跳信息到JobTracker,并接收和执行返回的各种任务命令,比如运行任务(launchTaskAction),提交任务(SubmitTaskAction),杀死任务(KillTaskAction),杀死job(KillJobAction),重新初始化(TaskTrackerReinitAction)。另一方面,接收Task的汇报信息并发送需要执行的任务命令。简单来说,就是两个功能:汇报心跳和执行命令。
一、TaskTracker启动过程分析
TaskTracker服务由类org.apache.hadoop.mapred.TaskTracker来实现。该类实现四个接口,分别是MRContents(常量接口),TaskUmbilicalProtocol(和Task通信协议接口),Runnable(线程接口,目的是以线程的方式启动TaskTracker),TaskTrackerMXBean(JMX MXBean,为了其他第三方监控软件监控)。
/**
* TaskTracker是一个独立的服务,所以有一个main方法。
*/
public static void main(String argv[]) throws Exception {
...... 打印信息,以及检查argv参数
try {
JobConf conf=new JobConf();
...... JMX信息设置
TaskTracker tt = new TaskTracker(conf); // 创建TaskTracker对象
MBeans.register("TaskTracker", "TaskTrackerInfo", tt); // 注册MXBean
tt.run(); // 执行
} catch (Throwable e) {
..... 异常处理
}
}
TaskTracker在构造函数中初始化一些重要的对象和线程,在run方法中维护一个和JobTracker通信的RPC连接。接下来分别分析构造函数和run方法。
1、重要变量和对象
TaskTracker中的成员变量主要作用是用于管理节点和监控节点上的任务。这里简单介绍一些比较有用的变量以及它们的默认值和意义。
变量名 | 变量修饰符 | 默认值 | 意义解释 |
running | volatile boolean | true | TaskTracker是否处于运行状态,true表示处于运行状态 |
lastCheckDirsTime | private long | 0 | 最近检查时间 |
lastNumFailures | private int | 0 | 最近检查失败个数 |
jobTrackAddr | InetSocketAddress | JobTracker的网络地址 | |
taskReportAddress | InetSocketAddress | TaskTracker和Task通信的网络地址 | |
taskReportServer | Server | RPC server,用于TaskTracker和Task间通信 | |
jobClient | InterTrackerProtocol | RPC client,用于和JobTracker通信 | |
heartbeatResponseId | short | -1 | 心跳响应id |
status | TaskTrackerStatus | null | TaskTrackerStatus状态 |
server | HttpServer | 将TaskTracker信息显示到web界面 | |
shuttingDown | volatile boolean | false | 关闭 |
tasks | Map<TaskAttemptID,TaskInProgress> | hashmap | 该节点上TID和TIP的映射关系 |
runningTasks | Map<TaskAttemptID,TaskInProgress> | null | 该节点上正在运行的TID和TIP的映射关系 |
runningJobs | Map<JobID,RunningJob> | treemap | 该节点上正在运行的作用列表,如果某个作业中的任务在该节点上运行,那么将该任务添加进入 |
acceptNewTasks | boolean | true | 是否接受新任务,每次给JobTracker汇报心跳的时候带上该值。 |
maxMapSlots | private int | 0 | 该节点上配置的Map slot数目 |
maxReduceSlots | private int | 0 | 该节点上配置的Reduce slot数目 |
failures | private int | 0 | 该节点上失败的job次数 |
heartbeatInterval | private volatile int | 3000(ms) | 心跳间隔时间,默认3s |
aclsManager | ACLsManager | 作业访问权限控制对象 | |
taskController | TaskController | 用于控制任务的初始化,终结和清洗工作 | |
userLogManager | UserLogManager | 用户日志管理 | |
jvmManager | JvmManager | JVM管理 | |
distributedCacheManager | TrackerDistributedCacheManager | 分布式缓存管理 | |
mapEventsFetcher | MapEventsFetcherThread | 获取以及完成的MapTask列表,为Reduce Task远程copy提供前期准备 | |
taskMemoryManager | TaskMemoryManagerThread | 任务内存监控 | |
taskLauncher | TaskLauncher | 启动任务 | |
localizer | Localizer | 任务本地化 | |
healthChecker | NodeHealthCheckerService | 节点健康状况检查 | |
jettyBugMonitor | JettyBugMonitor | 应对jetty中的bug启动的一个线程 |
这些变量和对象的初始化工作基本上均放在构造函数中完成,现在看看构造函数:
........
FILE_CACHE_SIZE = conf.getInt("mapred.tasktracker.file.cache.size", 2000); // 获取文件cache大小,默认2000
maxMapSlots = conf.getInt("mapred.tasktracker.map.tasks.maximum", 2); // 获取配置的map slot大小,默认2
maxReduceSlots = conf.getInt("mapred.tasktracker.reduce.tasks.maximum", 2); // 获取配置的reduce slot个数,默认为2
diskHealthCheckInterval = conf.getLong(DISK_HEALTH_CHECK_INTERVAL_PROPERTY, DEFAULT_DISK_HEALTH_CHECK_INTERVAL); // 获取节点健康检查间隔时间,默认为60s
..........
taskController = (TaskController) ReflectionUtils.newInstance(taskControllerClass, fConf); // 创建控制器对象
..........
initialize(); // 初始化
.........
server.start(); // 启动httpserver
this.httpPort = server.getPort();
checkJettyPort(httpPort); // 检查jetty服务器,由于当端口号为小于0的情况下,jetty有bug,所以在这种情况下,设置shuttingDown为true。
在构造函数中,除了获取一些基本的设置参数外,只初始化了taskController,server等。其他的初始化工作是调用方法initialize完成的。
// RPC initialization
int max = maxMapSlots > maxReduceSlots ? maxMapSlots : maxReduceSlots; // RPC初始值,采用最大的slot值。
//set the num handlers to max*2 since canCommit may wait for the duration of a heartbeat RPC
// 创建和Task通信的Server端对象,线程大小为2倍的slot大小。
this.taskReportServer = RPC.getServer(this, bindAddress, tmpPort, 2 * max, false, this.fConf, this.jobTokenSecretManager);
this.taskReportServer.start(); // 启动
........
// Initialize DistributedCache
this.distributedCacheManager = new TrackerDistributedCacheManager(this.fConf, taskController); // 初始化分布式缓存系统
this.distributedCacheManager.startCleanupThread(); // 启动 this.jobClient = (InterTrackerProtocol)UserGroupInformation.getLoginUser().doAs(
new PrivilegedExceptionAction<Object>() {
public Object run() throws IOException {
return RPC.waitForProxy(InterTrackerProtocol.class,InterTrackerProtocol.versionID,jobTrackAddr, fConf);
}
}); // 创建和JobTracker通信的客户端对象
......
// 创建获取map的线程
this.mapEventsFetcher = new MapEventsFetcherThread();
mapEventsFetcher.setDaemon(true);
mapEventsFetcher.setName("Map-events fetcher for all reduce tasks " + "on " + taskTrackerName);
mapEventsFetcher.start();
.............
// 创建map/reduce任务启动线程并run
mapLauncher = new TaskLauncher(TaskType.MAP, maxMapSlots);
reduceLauncher = new TaskLauncher(TaskType.REDUCE, maxReduceSlots);
mapLauncher.start();
reduceLauncher.start();
我们可以看到基本上所有涉及到的变量或者对象都已经完成了初始化并启动,至此TaskTracker的初始化工作基本完成。
二、和JobTracker的交互
1、单次心跳发送
Hadoop中的所有心跳机制均采用pull通信模型,即master从不会主动与slave通信,而总是被动的等待slave汇报信息并领取其对应的命令。TaskTracker周期性的向JobTracker汇报信息就是这种模式。
在TaskTracker中的run方法中,维护一个无线循环,用于通过心跳发送状态信息和接收命令,代码如下:
boolean denied = false; // 是否JobTracker拒绝TaskTracker的心跳报告
while (running && !shuttingDown && !denied) { // 如果当然TaskTracker处于运行状态,而且jetty服务器运行正常,并且没有被JobTracker拒绝请求,那么就一直循环
boolean staleState = false; // 状态信息
try {
// This while-loop attempts reconnects if we get network errors
while (running && !staleState && !shuttingDown && !denied) { // 外面的条件加上如果状态信息为false,那么就一直循环,这种方式是为了保证,当网络出现问题的时候,能够重新连接。
try {
State osState = offerService(); // 进行具体和JobTracker的通信
if (osState == State.STALE) { // 返回的状态不正常
staleState = true;
} else if (osState == State.DENIED) { // 返回的状态不正常
denied = true;
}
} catch (Exception ex) {
// 异常处理
}
}
} finally {
close(); // 只要是出现异常,或者是退出死循环,那么都关闭资源,重新启动资源
}
if (shuttingDown) { return; } // 如果是jetty服务器问题,那么直接退出
initialize(); // 网络出现问题,重新连接
}
if (denied) { // 如果JobTracker拒绝请求,那么关闭httpserver
shutdown();
}
其中offerService中的函数代码框架是:
while (running && !shuttingDown) {// Send the heartbeat and process the jobtracker's directives
// 发送心跳报告,并获取任务命令
HeartbeatResponse heartbeatResponse = transmitHeartBeat(now); if (actions != null){ // 如果命令不为空,那就处理命令
for(TaskTrackerAction action: actions) {
if (action instanceof LaunchTaskAction) { // 如果命令为启动task命令
addToTaskQueue((LaunchTaskAction)action);
} else if (action instanceof CommitTaskAction) { // 提交task命令
...
} else {
....
}
}
}
markUnresponsiveTasks(); // 杀死一定时间内无反应的任务
killOverflowingTasks(); // 当磁盘空间少于mapred.local.dir.minspacekill时候,寻找合适的任务将其杀死以释放空间。
TaskTracker的单次心跳发送过程为:
步骤1: 判断是否到达心跳发送时间
心跳发送时间由集群的规模和任务的运行情况来决定。JobTracker能够根据当前集群的规模和状态动态调整心跳间隔,并将下一次的心跳间隔时间放到应答中返回给TaskTracker。任务运行情况,当TaskTracker发送有任务或者job执行失败,就会立即缩短间隔时间,以便将任务完成或者任务失败的信息发送给JobTracker,进而可以快速的分配新的任务,主要作用是为了提高任务的响应时间和资源的利用率。需要配置参数:
mapreduce.tasktracker.outofband.beartbeat: 决定是否启用,默认为flase,不启用。
mapreduce.tasktracker.outofband.hearbeat.damper: 心跳收缩因子。默认值为10000。
计算时间间隔算法为:(heartbeatInterval / (numFinishedTasks * oobHeartbeatDamper + 1)),其中heartbeatInterval为从JobTracker端获取的心跳间隔时间(默认为3000),numberFinishedTasks为完成的任务个数,oobHeartbeatDammper为收缩因子的值。
步骤2: 如果TaskTracker刚启动,检查TaskTracker和JobTracker的版本信息是否一致
只有与JobTracker的版本信息一致,才可以发送心跳报告。版本信息由hadoop版本号,修订版本号,代码编译用户和校验和四部分组成。比如:"1.0.0 from 12555 by kashad source checksum 423dcd5a752eddd8e45ead6fd5ff9a24",hadoop版本号2.5.0,修订版本号为12555,编译者为kashad,校验和为423dcd5a752eddd8e45ead6fd5ff9a24。
步骤3: 检测磁盘是否损坏
由于存储中间结果的输入目录(mapred.local.dir)是存储在本地的,而且没有备份文档,所以当磁盘出现损坏的时候,需要TaskTracker重新初始化。TaskTracker周期性(间隔由mapred.disk.healthChecker.interval决定,默认为60s)的检查磁盘是否损坏。
步骤4: 发送心跳
TaskTracker将当前节点的运行信息发送给JobTracker,同时接收JobTracker的各种命令。
步骤5: 接收并执行各种命令
主要分为三种:job重新运行命令,TaskTracker重新初始化命令,其他命令。
待续................
[Hadoop] - TaskTracker源码分析的更多相关文章
- [Hadoop] - TaskTracker源码分析(状态发送)
TaskTracker节点向JobTracker汇报当前节点的运行时信息时候,是将运行状态信息同心跳报告一起发送给JobTracker的,主要包括TaskTracker的基本信息.节点资源使用信息.各 ...
- [Hadoop] - TaskTracker源码分析(TaskTracker节点健康状况监控)
在TaskTracker中对象healthStatus保存了当前节点的健康状况,对应的类是org.apache.hadoop.mapred.TaskTrackerStatus.TaskTrackerH ...
- Hadoop RPC源码分析
Hadoop RPC源码分析 上一篇文章http://www.cnblogs.com/dycg/p/rpc.html 讲了Hadoop RPC的使用方法,这一次我们从demo中一层层进行分析. RPC ...
- Hadoop TextInputFormat源码分析
from:http://blog.csdn.net/lzm1340458776/article/details/42707047 InputFormat主要用于描述输入数据的格式(我们只分析新API, ...
- Hadoop TaskScheduler源码分析
TaskScheduler是MapReduce中的任务调度器.在MapReduce中,JobTracker接收JobClient提交的Job,将它们按InputFormat的划分以及其他相关配置,生成 ...
- Hadoop2源码分析-准备篇
1.概述 我们已经能够搭建一个高可用的Hadoop平台了,也熟悉并掌握了一个项目在Hadoop平台下的开发流程,基于Hadoop的一些套件我们也能够使用,并且能利用这些套件进行一些任务的开发.在Had ...
- hadoop的RPC机制 -源码分析
这些天一直奔波于长沙和武汉之间,忙着腾讯的笔试.面试,以至于对hadoop RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上 ...
- Hadoop心跳机制源码分析
正文: 一.体系背景 首先和大家说明一下:hadoop的心跳机制的底层是通过RPC机制实现的,这篇文章我只介绍心跳实现的代码,对于底层的具体实现,大家可以参考我的另几篇博客: 1. hadoop的RP ...
- Hadoop的RPC机制源码分析
分析对象: hadoop版本:hadoop 0.20.203.0 必备技术点: 1. 动态代理(参考 :http://www.cnblogs.com/sh425/p/6893662.html )2. ...
随机推荐
- java 服务
2014年2月27日 10:30:55 学习 Java Service Wrapper 2014年2月27日 14:14:14太复杂了,以后研究吧. ###### 2014年2月27日 14:14:2 ...
- python 数组的del ,remove,pop区别
以a=[1,2,3] 为例,似乎使用del, remove, pop一个元素2 之后 a都是为 [1,3], 如下: >>> a=[1,2,3] >>> a.rem ...
- chrome的功能Copy as cURL
https://segmentfault.com/q/1010000002508961
- PHP实现仿Google分页效果的分页函数
本文实例讲述了PHP实现仿Google分页效果的分页函数.分享给大家供大家参考.具体如下: /** * 分页函数 * @param int $total 总页数 * @param int $pages ...
- java web几种开发模式(转)
Java Web开发方案有多种可供选择,这里列举一些经典的开发模式进行横向比较,为Java Web的开发模式选择提供参考.除此之外还有好多方案(如Tapestry和Wicket)并不了解,这里就不列举 ...
- Struts框架中struts-config.xml文件配置小结
弄清楚struts-config.xml中各项元素的作用,对于我们构建web项目有莫大的好处.<struts-config>是struts的根元素,它主要有8个子元素,DTD定义 如下: ...
- Delphi TRect函数例子
{ 在网上看到个这个例子感觉还不错,将它移到自己的博客里没事的时候看看: TRect 作用:保存一个矩形的左上角和右下角的坐标值: 声明: type TRect = ...
- --@ui-router--登录页通过路由跳转到内页的demo
今天还是来说一下angular中的路由模块.我们实际项目中,各个页面的切换是经常会与Auth相关的.比如我网站的后台,是需要登录过的用户才能进去,那么我们用angularJS做前端路由的时候应该怎么完 ...
- C# winform ListView+CheckBox的做法
1.设置ListView的属性:CheckBoxs=true 2.ListView字段第一列文本框为空,把工具箱里面的CheckBox控件拖到ListView的第一个字段做全选/全不选的控件. 3.C ...
- Pomelo的component组件
pomelo的核心是由一系列松耦合的component组成,同时我们也可以实现我们自己的component来完成一些自己定制的功能.对于我们的聊天应用,我们尝试给其增加一个component,目的是展 ...