【Hadoop代码笔记】Hadoop作业提交之TaskTracker获取Task

一、概要描述

上上一篇博文上一篇博文中分别描述了jobTracker和其服务(功能)模块初始化完成后,接收JobClient提交的作业,并进行初始化。本文着重描述,JobTracker如何选择作业的Task分发到TaskTracker。本文只是描述一个TaskTracker如何从JobTracker获取Task任务。Task任务在TaskTracker如何执行将在后面博文中描述。

二、 流程描述  

1. TaskTracker在run中调用offerService()方法一直死循环的去连接Jobtracker,先Jobtracker发送心跳,发送自身状态,并从Jobtracker获取任务指令来执行。

2. 在JobTracker的heartbeat方法中,对于来自每一个TaskTracker的心跳请求,根据一定的作业调度策略调用assignTasks方法选择一定Task

3.Scheduler调用对应的LoadManager的canAssignMap方法和canAssignReduce方法以决定是否可以给tasktracker分配任务。默认的是CapBasedLoad,全局平均分配。即根据全局的任务槽数,全局的map任务数的比值得到一个load系数,该系数乘以待分配任务的tasktracker的最大map任务数,即是该tasktracker能分配得到的任务数。如果太tracker当前运行的任务数小于可运行的任务数,则任务可以分配新作业给他。(图中缺失了LoadManager的表达,也画不下了,就不加了。在代码详细分析中有)

3. Scheduler的调用TaskSelector的obtainNewMapTask或者obtainNewReduceTask选择Task。

4. 在DefaultTaskSelector中选择Task的方法其实只是封装了JobInProgress的对应方法。

5. JobTracker根据得到的Task构造TaskTrackerAction设置到到HeartbeatResponse返回给TaskTracker。

6. TaskTracker中将来自JobTracker的任务加入到TaskQueue中等待执行。

三、代码详细

1.  TaskTracker的入口函数main

  JobConf conf=new JobConf();
// enable the server to track time spent waiting on locks
ReflectionUtils.setContentionTracing
(conf.getBoolean("tasktracker.contention.tracking", false));
new TaskTracker(conf).run();

2. TaskTracker的构造函数

maxCurrentMapTasks = conf.getInt(
"mapred.tasktracker.map.tasks.maximum", 2);
maxCurrentReduceTasks = conf.getInt(
"mapred.tasktracker.reduce.tasks.maximum", 2);
this.jobTrackAddr = JobTracker.getAddress(conf); //启动httpserver 展示tasktracker状态。
this.server = new HttpServer("task", httpBindAddress, httpPort,
httpPort == 0, conf);
server.start();
this.httpPort = server.getPort();
//初始化方法
initialize();

3. TaskTracker的initialize方法,完成TaskTracker的初始化工作。

主要流程

1)         检查可以创建本地文件夹

2)         清理或者初始化需要用到的实例集合变量

3)         初始化RPC服务器,接受task的请求。

4)         清除临时文件

5)         jobtracker的代理,负责处理和jobtracker的交互,通过RPC方式。

6)         一个线程,获取map完成事件。

7)         初始化内存管理

8)         分别启动map和reduce的tasklauncher

 synchronized void initialize()
{
//检查可以创建本地文件夹
checkLocalDirs(this.fConf.getLocalDirs());
fConf.deleteLocalFiles(SUBDIR);
//清理或者初始化需要用到的实例集合变量
this.tasks.clear();
this.runningTasks = new LinkedHashMap<TaskAttemptID, TaskInProgress>();
this.runningJobs = new TreeMap<JobID, RunningJob>();
this.jvmManager = new JvmManager(this);
//初始化RPC服务器,接受task的请求。
this.taskReportServer =
RPC.getServer(this, bindAddress, tmpPort, 2 * max, false, this.fConf);
this.taskReportServer.start();
// 清除临时文件
DistributedCache.purgeCache(this.fConf);
cleanupStorage(); //jobtracker的代理,负责处理和jobtracker的交互,通过RPC方式。
this.jobClient = (InterTrackerProtocol)
RPC.waitForProxy(InterTrackerProtocol.class,
InterTrackerProtocol.versionID,
jobTrackAddr, this.fConf); //一个线程,获取map完成事件。
this.mapEventsFetcher = new MapEventsFetcherThread();
mapEventsFetcher.setDaemon(true);
mapEventsFetcher.setName(
"Map-events fetcher for all reduce tasks " + "on " + taskTrackerName);
mapEventsFetcher.start();
//初始化内存管理
initializeMemoryManagement();
//分别启动map和reduce的tasklauncher
mapLauncher = new TaskLauncher(maxCurrentMapTasks);
reduceLauncher = new TaskLauncher(maxCurrentReduceTasks);
mapLauncher.start();
reduceLauncher.start(); }

4. TaskTracker run方法,在其中一直尝试执行offerService方法

public void run()
{
while (running && !staleState && !shuttingDown && !denied) {
State osState = offerService();
}
}

5. TaskTracker 的offerService方法

1)   通过RPC调用获得Jobtracker的系统目录。

2)   发送心跳并且获取Jobtracker的应答

3)   从JobTrackeer的应答中获取指令

4)   不同的指令类型执行不同的动作

5)   对于要launch的task加入到taskQueue中去

6)   对于清理动作,加入待清理的task集合,会有线程自动清理

7)   杀死那些过久未反馈进度的task

8)   当磁盘空间不够时,杀死某些task以腾出空间

        State offerService()
{
//通过RPC调用获得Jobtracker的系统目录。
String dir = jobClient.getSystemDir();
if (dir == null) {
throw new IOException("Failed to get system directory");
}
systemDirectory = new Path(dir);
systemFS = systemDirectory.getFileSystem(fConf);
}
// 发送心跳并且获取Jobtracker的应答
HeartbeatResponse heartbeatResponse = transmitHeartBeat(now);
//从JobTrackeer的应答中获取指令
TaskTrackerAction[] actions = heartbeatResponse.getActions();
//不同的指令类型执行不同的动作
if (actions != null){
for(TaskTrackerAction action: actions) {
//对于要launch的task加入到taskQueue中去
if (action instanceof LaunchTaskAction) {addToTaskQueue((LaunchTaskAction)action); } else if (action instanceof CommitTaskAction) {
CommitTaskAction commitAction = (CommitTaskAction)action;
if (!commitResponses.contains(commitAction.getTaskID())) {commitResponses.add(commitAction.getTaskID());}
//加入待清理的task集合,会有线程自动清理
} else {tasksToCleanup.put(action);
}
}
}
//杀死那些过久未反馈进度的task
markUnresponsiveTasks();
//当磁盘空间不够时,杀死某些task以腾出空间
killOverflowingTasks();
}

6. TaskTracker的 transmitHeartBeat方法,定时向JobTracker发心跳。其实是通过RPC的方式向调用Jobtracker的heartbeat方法。

private HeartbeatResponse transmitHeartBeat(long now)
{
boolean askForNewTask;
long localMinSpaceStart;
synchronized (this) {
//判断该Tasktracker是否可以接受新的task,依赖于
askForNewTask = (status.countMapTasks() < maxCurrentMapTasks ||
status.countReduceTasks() < maxCurrentReduceTasks) &&
acceptNewTasks;
localMinSpaceStart = minSpaceStart;
}
if (askForNewTask) {
checkLocalDirs(fConf.getLocalDirs());
//判断本地空间是否足够,以决定是否接受新的task
askForNewTask = enoughFreeSpace(localMinSpaceStart);
long freeDiskSpace = getFreeSpace();
long totVmem = getTotalVirtualMemoryOnTT();
long totPmem = getTotalPhysicalMemoryOnTT();
status.getResourceStatus().setAvailableSpace(freeDiskSpace); status.getResourceStatus().setTotalVirtualMemory(totVmem); status.getResourceStatus().setTotalPhysicalMemory(totPmem); status.getResourceStatus().setMapSlotMemorySizeOnTT(mapSlotMemorySizeOnTT); status.getResourceStatus().setReduceSlotMemorySizeOnTT(reduceSlotSizeMemoryOnTT);
}
//通过jobclient通过RPC的方式向调用Jobtracker的heartbeat方法。
HeartbeatResponse heartbeatResponse = jobClient.heartbeat(status, ustStarted,justInited, askForNewTask, heartbeatResponseId);
}

 6. JobTracker的 heartbeat方法。Jobtracker 接受并处理 tasktracker上报的状态,在返回的应答信息中指示tasktracker完成启停job或启动某个task的动作。 

动作类型类

描述

CommitTaskAction

指示Task保存输出,即提交

KillJobAction

杀死属于这个Job的任何一个Task

KillTaskAction

杀死指定的Task

LaunchTaskAction

开启某个task

ReinitTrackerAction

重新初始化taskTracker

主要流程如下:

1)       acceptTaskTracker(status)方法通过查询inHostsList(status) && !inExcludedHostsList确认Tasktracker是否在JobTracker的允许列表中。

2)       当得知TaskTracker重启的标记,从jobtracker的潜在故障名单中移除该tasktracker

3)       如果initialContact为否表示这次心跳请求不是该taskTracker第一次连接jobtracker,但是如果在jobtracker的trackerToHeartbeatResponseMap记录中没有之前的响应记录,则说明发生了笔记严重的错误。发送指令给tasktracker要求其重新初始化。

4)       如果这是有问题的tasktracker重新接回来的第一个心跳,则通知recoveryManager recoveryManager从的recoveredTrackers列表中移除该tracker以表示该tracker又正常的接回来了。

5)       如果initialContact != true 并且 revHeartbeatResponse != null表示上一个心跳应答存在,但是tasktracker表示第一次请求,则说上一个initialContact请求的应答丢失了,未传送到tasktracker。则只是简单的把原来的应答重发一下即可。

6)       构造应答的Id,是递加的。

7)       处理心跳,其实就是在jobTracker端更新该tasktracker的状态

8)       检查tasktracker可以运行新的task

9)       调用JobTracker配置的taskSceduler来调度task给对应的TaskTracker。从submit到JobTracker的Job列表中选择每个job的每个Task,适合交给该TaskTracker调度的Task

10)  把分配的Task加入到expireLaunchingTasks,监视并处理其是否超时。

11)  根据调度器发获得要启动的task构造LaunchTaskAction,通知taskTracker启动这些task。

12)  把属于该tasktracker的,job已经结束的task加入到killTasksList,发送到tasktracker杀死。即结束那些在tasktracker上已经结束了的作业的task,不管作业是完成还失败。

13)  判定哪些作业需要清理的,构造Action加入到action列表中。trackerToJobsToCleanup是一个结合,当job gc的时候,调用 finalizeJob进而调用 addJobForCleanup 把作业加入到trackerToJobsToCleanup中

14)  判定那些task可以提交输出,构造action加入到action列表。

15)  计算下一次心跳的间隔,设置到应答消息中。

16)  把上面这些Action设置到response中返回。

17)  把本次应答保存到trackerToHeartbeatResponseMap中

  1 public synchronized HeartbeatResponse heartbeat(TaskTrackerStatus status,
2 boolean restarted,
3 boolean initialContact,
4 boolean acceptNewTasks,
5 short responseId)
6 throws IOException {
7
8 //1) acceptTaskTracker(status)方法通过查询inHostsList(status) && !inExcludedHostsList确认Tasktracker是否在JobTracker的允许列表中。
9 if (!acceptTaskTracker(status)) {
10 throw new DisallowedTaskTrackerException(status);
11 }
12 String trackerName = status.getTrackerName();
13 long now = System.currentTimeMillis();
14 boolean isBlacklisted = false;
15 if (restarted) {
16 //2)当得知TaskTracker重启的标记,从jobtracker的潜在故障名单中移除该tasktracker
17 faultyTrackers.markTrackerHealthy(status.getHost());
18 } else {
19 isBlacklisted =
20 faultyTrackers.shouldAssignTasksToTracker(status.getHost(), now);
21 }
22
23 HeartbeatResponse prevHeartbeatResponse =trackerToHeartbeatResponseMap.get(trackerName);
24 boolean addRestartInfo = false;
25
26 if (initialContact != true) {
27 //3)如果initialContact为否表示这次心跳请求不是该taskTracker第一次连接jobtracker,但是如果在jobtracker的trackerToHeartbeatResponseMap记录中没有之前的响应记录,则说明发生了笔记严重的错误。发送指令给tasktracker要求其重新初始化。
28 if (prevHeartbeatResponse == null) {
29 // This is the first heartbeat from the old tracker to the newly
30 // started JobTracker
31 //4)如果这是有问题的tasktracker重新接回来的第一个心跳,则通知recoveryManager
32 if (hasRestarted()) {
33 addRestartInfo = true;
34 // recoveryManager从的recoveredTrackers列表中移除该tracker以表示该tracker又正常的接回来了。
35 recoveryManager.unMarkTracker(trackerName);
36 } else {
37 //发送指令让tasktracker重新初始化。
38 return new HeartbeatResponse(responseId,
39 new TaskTrackerAction[] {new ReinitTrackerAction()});
40 }
41
42 } else {
43
44 //如果initialContact != true 并且 revHeartbeatResponse != null表示上一个心跳应答存在,但是tasktracker表示第一次请求,则说上一个initialContact请求的应答丢失了,未传送到tasktracker。则只是简单的把原来的应答重发一下即可。
45 if (prevHeartbeatResponse.getResponseId() != responseId) {
46 LOG.info("Ignoring 'duplicate' heartbeat from '" +
47 trackerName + "'; resending the previous 'lost' response");
48 return prevHeartbeatResponse;
49 }
50 }
51 }
52
53 // 应答的Id是递加的。
54 short newResponseId = (short)(responseId + 1);
55 status.setLastSeen(now);
56 //处理心跳,其实就是在jobTracker端更新该tasktracker的状态
57 if (!processHeartbeat(status, initialContact)) {
58 if (prevHeartbeatResponse != null) {
59 trackerToHeartbeatResponseMap.remove(trackerName);
60 }
61 return new HeartbeatResponse(newResponseId,
62 new TaskTrackerAction[] {new ReinitTrackerAction()});
63 }
64
65 // 检查tasktracker可以运行新的task
66 if (recoveryManager.shouldSchedule() && acceptNewTasks && !isBlacklisted) {
67 TaskTrackerStatus taskTrackerStatus = getTaskTracker(trackerName);
68 if (taskTrackerStatus == null) {
69 } else {
70 List<Task> tasks = getSetupAndCleanupTasks(taskTrackerStatus);
71 if (tasks == null ) {
72 //2调用JobTracker配置的taskSceduler来调度task给对应的TaskTracker。从submit到JobTracker的Job列表中选择每个job的每个Task,适合交给该TaskTracker调度的Task
73
74 tasks = taskScheduler.assignTasks(taskTrackerStatus);}
75 if (tasks != null) {
76 //把分配的Task加入到expireLaunchingTasks,监视并处理其是否超时。
77 for (Task task : tasks) {
78 Object expireLaunchingTasks;
79 expireLaunchingTasks.addNewTask(task.getTaskID());
80 actions.add(new LaunchTaskAction(task));
81 }
82 }
83 }
84 }
85
86 //把属于该tasktracker的,job已经结束的task加入到killTasksList,发送到tasktracker杀死。即结束那些在tasktracker上已经结束了的作业的task,不管作业是完成还失败。
87 List<TaskTrackerAction> killTasksList = getTasksToKill(trackerName);
88 if (killTasksList != null) {
89 actions.addAll(killTasksList);
90 }
91
92 //判定哪些作业需要清理。finalizeJob-> addJobForCleanup 当gc一个job的时候,会调用以上方法把其加入到trackerToJobsToCleanup中
93 List<TaskTrackerAction> killJobsList = getJobsForCleanup(trackerName);
94 if (killJobsList != null) {
95 actions.addAll(killJobsList);
96
97 //判定那些task可以提交输出。
98 List<TaskTrackerAction> commitTasksList = getTasksToSave(status);
99 if (commitTasksList != null) {
100 actions.addAll(commitTasksList);
101 }
102
103 //calculate next heartbeat interval and put in heartbeat response
104 //计算下一次心跳的间隔,设置到应答消息中。
105 int nextInterval = getNextHeartbeatInterval();
106 response.setHeartbeatInterval(nextInterval);
107
108 //把上面这些Action设置到response中返回。
109 response.setActions(actions.toArray(new TaskTrackerAction[actions.size()]));
110 //把本次应答保存到trackerToHeartbeatResponseMap中
111 trackerToHeartbeatResponseMap.put(trackerName, response);
112 return response;
113
114 }

7.FairScheduler的assignTasks方法。JobTracker就是调用该方法来实现作业的分配的。

主要流程如下:

1)        分别计算可运行的maptask和reducetask总数

2)        ClusterStatus 维护了当前Map/Reduce作业框架的总体状况。根据ClusterStatus计算得到获得map task的槽数,reduce task的槽数。

3)        调用LoadManager方法决定是否可以为该tasktracker分配任务(默认CapBasedLoadManager方法根据全局的任务槽数,全局的map任务数的比值得到一个load系数,该系数乘以待分配任务的tasktracker的最大map任务数,即是该tasktracker能分配得到的任务数。如果太tracker当前运行的任务数小于可运行的任务数,则任务可以分配新作业给他)

4)        从job列表中找出那些job需要运行map或reduce任务,加到List<JobInProgress> candidates集合中

5)        对candidates集合中的job排序,对每个job调用taskSelector的obtainNewMapTask或者obtainNewReduceTask方法获取要执行的task。把所以的task放到task集合中返回。从而实现了作业Job的任务Task分配。

6)        并对candidates集合中的每个job,更新Jobinfo信息,即其正在运行的task数,需要运行的task数,以便其后续调度用。

 1     public synchronized List<Task> assignTasks(TaskTrackerStatus tracker)
2 throws IOException {
3 if (!initialized) // Don't try to assign tasks if we haven't yet started up
4 return null;
5
6 oolMgr.reloadAllocsIfNecessary();
7
8 // 分别计算可运行的maptask和reducetask总数
9 int runnableMaps = 0;
10 int runnableReduces = 0;
11 for (JobInProgress job: infos.keySet()) {
12 runnableMaps += runnableTasks(job, TaskType.MAP);
13 runnableReduces += runnableTasks(job, TaskType.REDUCE);
14 }
15
16 // ClusterStatus 维护了当前Map/Reduce作业框架的总体状况。
17 ClusterStatus clusterStatus = taskTrackerManager.getClusterStatus();
18 //计算得到获得map task的槽数,reduce task的槽数。
19 int totalMapSlots = getTotalSlots(TaskType.MAP, clusterStatus);
20 int totalReduceSlots = getTotalSlots(TaskType.REDUCE, clusterStatus);
21
22 //从job列表中找出那些job需要运行map或reduce任务,加到List<JobInProgress> candidates集合中
23 ArrayList<Task> tasks = new ArrayList<Task>();
24 TaskType[] types = new TaskType[] {TaskType.MAP, TaskType.REDUCE};
25 for (TaskType taskType: types) {
26 boolean canAssign = (taskType == TaskType.MAP) ?
27 //CapBasedLoadManager方法根据全局的任务槽数,全局的map任务数的比值得到一个load系数,该系数乘以待分配任务的tasktracker的最大map任务数,即是该tasktracker能分配得到的任务数。如果太tracker当前运行的任务数小于可运行的任务数,则任务可以分配新作业给他
28 loadMgr.canAssignMap(tracker, runnableMaps, totalMapSlots) :
29 loadMgr.canAssignReduce(tracker, runnableReduces, totalReduceSlots);
30 if (canAssign) {
31 List<JobInProgress> candidates = new ArrayList<JobInProgress>();
32 for (JobInProgress job: infos.keySet()) {
33 if (job.getStatus().getRunState() == JobStatus.RUNNING &&
34 neededTasks(job, taskType) > 0) {
35 candidates.add(job);
36 }
37 }
38 //对candidates集合中的job排序,对每个job调用taskSelector的obtainNewMapTask或者obtainNewReduceTask方法获取要执行的task。把所以的task放到task集合中返回。
39 // Sort jobs by deficit (for Fair Sharing) or submit time (for FIFO)
40 Comparator<JobInProgress> comparator = useFifo ?
41 new FifoJobComparator() : new DeficitComparator(taskType);
42 Collections.sort(candidates, comparator);
43 for (JobInProgress job: candidates) {
44 Task task = (taskType == TaskType.MAP ?
45 taskSelector.obtainNewMapTask(tracker, job) :
46 taskSelector.obtainNewReduceTask(tracker, job));
47 if (task != null) {
48 //并对candidates集合中的每个job,更新Jobinfo信息,即其正在运行的task数,需要运行的task数。
49 JobInfo info = infos.get(job);
50 if (taskType == TaskType.MAP) {
51 info.runningMaps++;
52 info.neededMaps--;
53 } else {
54 info.runningReduces++;
55 info.neededReduces--;
56 }
57 tasks.add(task);
58 if (!assignMultiple)
59 return tasks;
60 break;
61 }
62 }
63 }
64 }
65
66 // If no tasks were found, return null
67 return tasks.isEmpty() ? null : tasks;
68 }

 8.CapBasedLoadManager的canAssignMap方法和canAssignReduce方法。一种简单的算法在FairScheduler中用来决定是否可以给某个tasktracker分配maptask或者reducetask。总体思路是对于某种类型的task,map或者reduce,考虑jobtracker管理的mapreduce集群全部的任务数,和全部的任务槽数,和该tasktracker上面当前的任务数,以决定是否给他分配任务。如对于maptask,根据全局的任务槽数,全局的map任务数的比值得到一个load系数,该系数乘以待分配任务的tasktracker的最大map任务数,即是该tasktracker能分配得到的任务数。如果太tracker当前运行的任务数小于可运行的任务数,则任务可以分配新作业给他。reducetask同理。即尽量做到全局平均。

int getCap(int totalRunnableTasks, int localMaxTasks, int totalSlots) {
double load = ((double)totalRunnableTasks) / totalSlots;
return (int) Math.ceil(localMaxTasks * Math.min(1.0, load));
} @Override
public boolean canAssignMap(TaskTrackerStatus tracker,
int totalRunnableMaps, int totalMapSlots) {
return tracker.countMapTasks() < getCap(totalRunnableMaps,
tracker.getMaxMapTasks(), totalMapSlots);
} @Override
public boolean canAssignReduce(TaskTrackerStatus tracker,
int totalRunnableReduces, int totalReduceSlots) {
return tracker.countReduceTasks() < getCap(totalRunnableReduces,
tracker.getMaxReduceTasks(), totalReduceSlots);
}

9.   DefaultTaskSelector继承自TaskSelector,其两个方法其实只是对jobInprogress得封装,没有做什么特别的事情。

 @Override
public Task obtainNewMapTask(TaskTrackerStatus taskTracker, JobInProgress job)
throws IOException {
ClusterStatus clusterStatus = taskTrackerManager.getClusterStatus();
int numTaskTrackers = clusterStatus.getTaskTrackers();
return job.obtainNewMapTask(taskTracker, numTaskTrackers,
taskTrackerManager.getNumberOfUniqueHosts());
} @Override
public Task obtainNewReduceTask(TaskTrackerStatus taskTracker, JobInProgress job)
throws IOException {
ClusterStatus clusterStatus = taskTrackerManager.getClusterStatus();
int numTaskTrackers = clusterStatus.getTaskTrackers();
return job.obtainNewReduceTask(taskTracker, numTaskTrackers,
taskTrackerManager.getNumberOfUniqueHosts());
}

10. JobInProgress的obtainNewMapTask方法。其实主要逻辑是在findNewMapTask方法中实现。

public synchronized Task obtainNewMapTask(TaskTrackerStatus tts,
int clusterSize,
int numUniqueHosts
) throws IOException { int target = findNewMapTask(tts, clusterSize, numUniqueHosts, anyCacheLevel,
status.mapProgress()); Task result = maps[target].getTaskToRun(tts.getTrackerName());
if (result != null) {
addRunningTaskToTIP(maps[target], result.getTaskID(), tts, true);
} return result;
}

11  JobInProgress的findNewMapTask方法。
根据待派发Task的TaskTracker根据集群中的TaskTracker数量(clusterSize),运行TraskTracker的服务器数(numUniqueHosts),该Job中map task的平均进度(avgProgress),可以调度map的最大水平(距离其实),选择一个task执行。考虑到map的本地化。

    private synchronized int findNewMapTask(final TaskTrackerStatus tts,
final int clusterSize,
final int numUniqueHosts,
final int maxCacheLevel,
final double avgProgress) {
String taskTracker = tts.getTrackerName();
TaskInProgress tip = null; //1)更新TaskTracker总数。
this.clusterSize = clusterSize; //2)如果这个TraskTracker上面之前有很多map都会失败,则返回标记,不分配给他。
if (!shouldRunOnTaskTracker(taskTracker)) {
return -1; //3) 检查该TaskTracker有足够的资源运行。估算output的方法有点意思,根据(job现有的map数+当前job的map数)*已完成map数*2*已完成的map的输出size/已经完成map的输入size,即根据完成估算总数。
long outSize = resourceEstimator.getEstimatedMapOutputSize();
long availSpace = tts.getResourceStatus().getAvailableSpace();
if(availSpace < outSize) {
LOG.warn("No room for map task. Node " + tts.getHost() +
" has " + availSpace +
" bytes free; but we expect map to take " + outSize);
return -1;
} // For scheduling a map task, we have two caches and a list (optional)
// I) one for non-running task
// II) one for running task (this is for handling speculation)
// III) a list of TIPs that have empty locations (e.g., dummy splits),
// the list is empty if all TIPs have associated locations // First a look up is done on the non-running cache and on a miss, a look
// up is done on the running cache. The order for lookup within the cache:
// 1. from local node to root [bottom up]
// 2. breadth wise for all the parent nodes at max level // We fall to linear scan of the list (III above) if we have misses in the
// above caches //4)获得jobTracker所在的Node
Node node = jobtracker.getNode(tts.getHost()); // I) Non-running TIP :
//5) 从未运行的作业集合中选择一个nonRunningMapCache 加入到运行集合runningMapCache中。加入时根据待添加的Task的split的位置信息,在runningMapCache中保存Node和Task集合的对应关系。 // 1. check from local node to the root [bottom up cache lookup]
// i.e if the cache is available and the host has been resolved
// (node!=null)
if (node != null) {
Node key = node;
int level = 0;
// maxCacheLevel might be greater than this.maxLevel if findNewMapTask is
// called to schedule any task (local, rack-local, off-switch or speculative)
// tasks or it might be NON_LOCAL_CACHE_LEVEL (i.e. -1) if findNewMapTask is
// (i.e. -1) if findNewMapTask is to only schedule off-switch/speculative
// tasks
//从taskTracker本地开始由近至远查找要加入的Task 到runningMapCache中。
int maxLevelToSchedule = Math.min(maxCacheLevel, maxLevel);
for (level = 0;level < maxLevelToSchedule; ++level) {
List <TaskInProgress> cacheForLevel = nonRunningMapCache.get(key);
if (cacheForLevel != null) {
tip = findTaskFromList(cacheForLevel, tts,
numUniqueHosts,level == 0);
if (tip != null) {
// 把该map任务加入到runningMapCache
scheduleMap(tip);
return tip.getIdWithinJob();
}
}
key = key.getParent();
} // Check if we need to only schedule a local task (node-local/rack-local)
if (level == maxCacheLevel) {
return -1;
}
} //2. Search breadth-wise across parents at max level for non-running
// TIP if
// - cache exists and there is a cache miss
// - node information for the tracker is missing (tracker's topology
// info not obtained yet) // collection of node at max level in the cache structure
Collection<Node> nodesAtMaxLevel = jobtracker.getNodesAtMaxLevel(); // get the node parent at max level
Node nodeParentAtMaxLevel =
(node == null) ? null : JobTracker.getParentNode(node, maxLevel - 1); for (Node parent : nodesAtMaxLevel) { // skip the parent that has already been scanned
if (parent == nodeParentAtMaxLevel) {
continue;
} List<TaskInProgress> cache = nonRunningMapCache.get(parent);
if (cache != null) {
tip = findTaskFromList(cache, tts, numUniqueHosts, false);
if (tip != null) {
// Add to the running cache
scheduleMap(tip); // remove the cache if empty
if (cache.size() == 0) {
nonRunningMapCache.remove(parent);
}
LOG.info("Choosing a non-local task " + tip.getTIPId());
return tip.getIdWithinJob();
}
}
} //搜索非本地Map
tip = findTaskFromList(nonLocalMaps, tts, numUniqueHosts, false);
if (tip != null) {
// Add to the running list
scheduleMap(tip); LOG.info("Choosing a non-local task " + tip.getTIPId());
return tip.getIdWithinJob();
} //
// II) Running TIP :
// if (hasSpeculativeMaps) {
long currentTime = System.currentTimeMillis(); // 1. Check bottom up for speculative tasks from the running cache
if (node != null) {
Node key = node;
for (int level = 0; level < maxLevel; ++level) {
Set<TaskInProgress> cacheForLevel = runningMapCache.get(key);
if (cacheForLevel != null) {
tip = findSpeculativeTask(cacheForLevel, tts,
avgProgress, currentTime, level == 0);
if (tip != null) {
if (cacheForLevel.size() == 0) {
runningMapCache.remove(key);
}
return tip.getIdWithinJob();
}
}
key = key.getParent();
}
} // 2. Check breadth-wise for speculative tasks for (Node parent : nodesAtMaxLevel) {
// ignore the parent which is already scanned
if (parent == nodeParentAtMaxLevel) {
continue;
} Set<TaskInProgress> cache = runningMapCache.get(parent);
if (cache != null) {
tip = findSpeculativeTask(cache, tts, avgProgress,
currentTime, false);
if (tip != null) {
// remove empty cache entries
if (cache.size() == 0) {
runningMapCache.remove(parent);
}
LOG.info("Choosing a non-local task " + tip.getTIPId()
+ " for speculation");
return tip.getIdWithinJob();
}
}
} // 3. Check non-local tips for speculation
tip = findSpeculativeTask(nonLocalRunningMaps, tts, avgProgress,
currentTime, false);
if (tip != null) {
LOG.info("Choosing a non-local task " + tip.getTIPId()
+ " for speculation");
return tip.getIdWithinJob();
}
} return -1; }

12  JobInProgress的obtainNewReduceTask方法返回一个ReduceTask,实际调用的是findNewReduceTask方法。

public synchronized Task obtainNewReduceTask(TaskTrackerStatus tts,
int clusterSize,
int numUniqueHosts
) throws IOException {
//判定有足够的map已经完成。,
if (!scheduleReduces()) {
return null;
} int target = findNewReduceTask(tts, clusterSize, numUniqueHosts,
status.reduceProgress());
Task result = reduces[target].getTaskToRun(tts.getTrackerName());
if (result != null) {
addRunningTaskToTIP(reduces[target], result.getTaskID(), tts, true);
} return result;
}

13 JobInProgress的findNewReduceTask方法,为指定的TaskTracker选择Reduce task。不用考虑本地化。

private synchronized int findNewReduceTask(TaskTrackerStatus tts,
int clusterSize,
int numUniqueHosts,
double avgProgress) {
String taskTracker = tts.getTrackerName();
TaskInProgress tip = null; // Update the last-known clusterSize
this.clusterSize = clusterSize;
// 该taskTracker可用性符合要求
if (!shouldRunOnTaskTracker(taskTracker)) {
return -1;
} //估算Reduce的输入,根据map的总输出来和reduce的个数来计算。
long outSize = resourceEstimator.getEstimatedReduceInputSize();
long availSpace = tts.getResourceStatus().getAvailableSpace();
if(availSpace < outSize) {
LOG.warn("No room for reduce task. Node " + taskTracker + " has " +
availSpace +
" bytes free; but we expect reduce input to take " + outSize); return -1; //see if a different TIP might work better.
} // 1. check for a never-executed reduce tip
// reducers don't have a cache and so pass -1 to explicitly call that out
tip = findTaskFromList(nonRunningReduces, tts, numUniqueHosts, false);
if (tip != null) {
scheduleReduce(tip);
return tip.getIdWithinJob();
} // 2. check for a reduce tip to be speculated
if (hasSpeculativeReduces) {
tip = findSpeculativeTask(runningReduces, tts, avgProgress,
System.currentTimeMillis(), false);
if (tip != null) {
scheduleReduce(tip);
return tip.getIdWithinJob();
}
} return -1;
}

14 TaskTracker 的addToTaskQueue方法。对于要launch的task加入到taskQueue中去,不同类型的Task有不同类型额launcher。

private void addToTaskQueue(LaunchTaskAction action) {
if (action.getTask().isMapTask()) {
mapLauncher.addToTaskQueue(action);
} else {
reduceLauncher.addToTaskQueue(action);
}
}

完。

为了转载内容的一致性、可追溯性和保证及时更新纠错,转载时请注明来自:http://www.cnblogs.com/douba/p/hadoop_mapreduce_tasktracker_retrieve_task.html。谢谢!

 
 
分类: hadoop

Hadoop作业提交之TaskTracker获取Task的更多相关文章

  1. 【Hadoop代码笔记】Hadoop作业提交之TaskTracker获取Task

    一.概要描述 在上上一篇博文和上一篇博文中分别描述了jobTracker和其服务(功能)模块初始化完成后,接收JobClient提交的作业,并进行初始化.本文着重描述,JobTracker如何选择作业 ...

  2. 【Hadoop代码笔记】Hadoop作业提交之TaskTracker 启动task

    一.概要描述 在上篇博文描述了TaskTracker从Jobtracker如何从JobTracker获取到要执行的Task.在从JobTracker获取到LaunchTaskAction后,执行add ...

  3. 【hadoop代码笔记】hadoop作业提交之汇总

    一.概述 在本篇博文中,试图通过代码了解hadoop job执行的整个流程.即用户提交的mapreduce的jar文件.输入提交到hadoop的集群,并在集群中运行.重点在代码的角度描述整个流程,有些 ...

  4. 【Hadoop代码笔记】Hadoop作业提交之Child启动reduce任务

    一.概要描述 在上篇博文描述了TaskTracker启动一个独立的java进程来执行Map任务.接上上篇文章,TaskRunner线程执行中,会构造一个java –D** Child address ...

  5. 【Hadoop代码笔记】Hadoop作业提交之Child启动map任务

    一.概要描述 在上篇博文描述了TaskTracker启动一个独立的java进程来执行Map或Reduce任务.在本篇和下篇博文中我们会关注启动的那个入口是org.apache.hadoop.mapre ...

  6. 【Hadoop代码笔记】Hadoop作业提交之JobTracker接收作业提交

    一.概要描述 在上一篇博文中主要描述了JobTracker接收作业的几个服务(或功能)模块的初始化过程.本节将介绍这些服务(或功能)是如何接收到提交的job.本来作业的初始化也可以在本节内描述,但是涉 ...

  7. 【Hadoop代码笔记】Hadoop作业提交之JobTracker等相关功能模块初始化

    一.概要描述 本文重点描述在JobTracker一端接收作业.调度作业等几个模块的初始化工作.想过模块的介绍会在其他文章中比较详细的描述.受理作业提交在下一篇文章中会进行描述. 为了表达的尽可能清晰一 ...

  8. 【Hadoop代码笔记】Hadoop作业提交之客户端作业提交

    1.      概要描述仅仅描述向Hadoop提交作业的第一步,即调用Jobclient的submitJob方法,向Hadoop提交作业. 2.      详细描述Jobclient使用内置的JobS ...

  9. 【hadoop代码笔记】Hadoop作业提交中EagerTaskInitializationListener的作用

    在整理FairScheduler实现的task调度逻辑时,注意到EagerTaskInitializationListener类.差不多应该是job提交相关的逻辑代码中最简单清楚的一个了. todo: ...

随机推荐

  1. 十天学Linux内核之第五天---有关Linux文件系统实现的问题

    原文:十天学Linux内核之第五天---有关Linux文件系统实现的问题 有时间睡懒觉了,却还是五点多醒了,不过一直躺倒九点多才算起来,昨晚一直在弄飞凌的嵌入式开发板,有些问题没解决,自己电脑系统的问 ...

  2. AspNet.WebAPI.OData.ODataPQ

    AspNet.WebAPI.OData.ODataPQ实现WebAPI的分页查询服务 AspNet.WebAPI.OData.ODataPQ实现WebAPI的分页查询服务-(个人拙笔) AspNet. ...

  3. Tsql查询执行顺序

    对于T-SQL编程,用得最广泛的,莫过于查询(Querying).要想写出高质量.高性能的查询语句,必须深入地了解逻辑查询处理. 一.逻辑查询处理的各个阶段 (5)SELECT DISTINCT TO ...

  4. 大约HR升级版的设计为组汇总

    该公司刚刚完成HR系统升级,系统从单一公司实现使用更多的公司使用变更.在一个月的时间升级,虽然很苦,但他们自己的系统架构的感觉获益,有以下的详细的见解: 一.MVC还是非常重要 系统框架是五年前用de ...

  5. DevExpress asp.net 导出Excel 自动开启迅雷问题,默认保存为aspx页面

    目前采取曲线救国策略: 利用MVC ..... <dx:ASPxGridView ID="ASPxGridView1" runat="server" Au ...

  6. MS SQL Server递归查询

    原文:MS SQL Server递归查询 刚才在论坛上看到网友一个要求.参考如下,Insus.NET分析一下,可以使用MS SQL Server的递归查询,得到结果.准备一张表: 根据网友提供的数据, ...

  7. 在ASP.NET MVC中使用IIS级别的URL Rewrite

    原文 在ASP.NET MVC中使用IIS级别的URL Rewrite 大约一年半前,我在博客上写过一系列关于URL Rewrite的文章(2.3.4),把ASP.NET平台上进行URL Rewrit ...

  8. STL在迭代的过程,删除指定的元素

    直接在Code.在 Picture #include <iostream> #include <list> using namespace std; // STL在迭代的过程中 ...

  9. PHP添加Mssql/sqlserver2000扩展,适用于Php5.2/Php5.3/Php5.4

    原文:PHP添加Mssql/sqlserver2000扩展,适用于Php5.2/Php5.3/Php5.4 本文介绍如何在Windows下配置php扩展. 本文链接 http://blog.csdn. ...

  10. python学习笔记(五岁以下儿童)深深浅浅的副本复印件,文件和文件夹

    python学习笔记(五岁以下儿童) 深拷贝-浅拷贝 浅拷贝就是对引用的拷贝(仅仅拷贝父对象) 深拷贝就是对对象的资源拷贝 普通的复制,仅仅是添加了一个指向同一个地址空间的"标签" ...