图说MapReduce源码--JobTracker.getSetupAndCleanupTasks 任务选择顺序

来源:互联网 发布:商家订单导出软件 编辑:程序博客网 时间:2024/06/06 16:26

Hadoop将一个作业分解后的任务分成计算型任务和辅助型任务计算型任务是实际处理数据的任务包括MapReduce任务由专门的任务调度器TaskScheduler对它们进行调度(分配任务). 辅助型任务通常不会处理实际的数据而是用于同步计算型任务和清理磁盘上无用的目录包括job-setup task, job-cleanup task, task-cleanup task. 其中job-setup-cleanup用于计算型任务开始运行同步标识和结束运行同步标识即初始化作业时的setupcleanup TIP. 

task-cleanup task用于清理失败的计算型任务已经写到磁盘上的部分结果


public enum TaskType {/** Enum for map, reduce, job-setup, job-cleanup, task-cleanup task types. */  MAP, REDUCE, JOB_SETUP, JOB_CLEANUP, TASK_CLEANUP}

一个正常的TaskTracker还有空闲的slot, acceptNewTasks=true, JobTracker会为TaskTracker分配新任务任务的选择顺序是

优先辅助型任务,然后计算型任务:   job-cleanup > task-cleanup > job-setup > map task > reduce task

  // All the known jobs.  (jobid->JobInProgress) 当MR客户端提交作业调用到JT.addJob时, 先放入jobs中, 然后加入监听器的队列中  Map<JobID, JobInProgress> jobs = Collections.synchronizedMap(new TreeMap<JobID, JobInProgress>());    // returns cleanup tasks first, then setup tasks.  synchronized List<Task> getSetupAndCleanupTasks(TaskTrackerStatus taskTracker) throws IOException {    if (isInSafeMode()) return null;  // Don't assign *any* new task in safemode    int maxMapTasks = taskTracker.getMaxMapSlots();// 可以运行的Map的最大数量    int maxReduceTasks = taskTracker.getMaxReduceSlots();    int numMaps = taskTracker.countOccupiedMapSlots();// 正在运行的Map数量    int numReduces = taskTracker.countOccupiedReduceSlots();    int numTaskTrackers = getClusterStatus().getTaskTrackers();    int numUniqueHosts = getNumberOfUniqueHosts();    Task t = null;    synchronized (jobs) {// addJob时, 将新提交的作业加入到jobs中      if (numMaps < maxMapTasks) {  // 只要还有剩余的槽, 就优先选择辅助型任务, 选择顺序依次: job-cleanup > task-cleanup > job-setup        for (Iterator<JobInProgress> it = jobs.values().iterator(); it.hasNext();) {          JobInProgress job = it.next();          t = job.obtainJobCleanupTask(taskTracker, numTaskTrackers, numUniqueHosts, true); // job-cleanup task          if (t != null) { return Collections.singletonList(t); }  // 一次只返回一个任务        }        for (Iterator<JobInProgress> it = jobs.values().iterator(); it.hasNext();) {          JobInProgress job = it.next();          t = job.obtainTaskCleanupTask(taskTracker, true);  // task-cleanup task          if (t != null) { return Collections.singletonList(t); }        }        for (Iterator<JobInProgress> it = jobs.values().iterator(); it.hasNext();) {          JobInProgress job = it.next();          t = job.obtainJobSetupTask(taskTracker, numTaskTrackers, numUniqueHosts, true);  // job-setup task          if (t != null) { return Collections.singletonList(t); }        }      }      if (numReduces < maxReduceTasks) {// map优先于reduce        // 类似于上面Map的处理, 只不过调用JIP的相应方法时, 最后一个参数为false表示不是map而是reduce任务      }    }    return null;  }

任务选择顺序解析

作业的运行顺序是: job-setup > map > reduce > job-cleanup. 而任务选择的顺序为什么是: job-cleanup > task-setup > job-setup > map > reduce?

目的: 这样可以让`运行完成的作业快速结束新提交的作业立刻进入运行状态`.  

 

问题: 根据作业的执行顺序, job-cleanup task一定是在job-setup task执行之后才会执行的job-setup没有执行完, job-cleanup task显然不能执行

那么为什么选择任务时job-cleanup task可以优先于job-setup? 

 

场景: 作业队列jobs保存了所有未完成的作业, TT上可以同时运行多个作业

job1执行到了job-cleanup [作业1即将完成了]

job2正执行到map, 由于执行map/reduce的时候会产生TaskAttempt, 会有些失败的任务或者TA完成其他的TA失效即job2task-cleanup.  

job3才刚刚开始job-setup [作业3刚刚要开始]

那么当这个TTJT请求新任务时如何选择TT上的任务TaskInProgress. (: job-cleanup, job-setup, task-cleanup都属于TaskInProgress.)   

 

分析

I. 优先选择job-cleanup task

我们知道作业运行过程中分为mapreduce, 任务在TT上运行需要占用槽位slot. 

当作业开始时作业会占用TT一定数量的槽位以满足作业的运行当作业完成时就会释放槽位

因此优先执行job-cleanup task, 不仅可以让该作业快速完成还可以释放作业占用的槽位以满足TT上其他等待槽位的作业的运行

 

II. 选择task-cleanup task

MapReduce任务运行过程中会有多个TaskAttempt执行相同的Task. 有些TaskAttempt会执行成功有些则会失败

对于失败的TaskAttempt产生的临时文件需要及时清理掉.

 

III. 选择job-setup task

如果都没有job-cleanup tasktask-cleanup task, 现在才开始选择job-setup task. 选择此类型任务后任务就可以开始正式运行

 

注意: getSetupAndCleanupTasks按照优先级只要成功获取到上述任何一种类型的任务就不会往下继续寻找了

也许你会问: job-setup还没有执行, job-cleanup不应该执行啊即使你获取到了你也不可能先执行job-cleanup这个明显违反了作业的执行顺序.  

其实getSetupAndCleanupTasks针对的是队列中的所有作业对于同一个作业显然必须严格按照作业执行顺序来选择任务的

但是到多个作业就不一定了多个作业之间不一定要一个作业全部执行完再执行另一个作业而是多个作业之间可以穿插运行

 

以上述场景为例如果优先执行job3job-setup task. 有可能job1因为还没有完成正在占用着任务需要的槽位

在槽位不够的情况下, job1只能等到有空闲的槽位才可以获得执行的机会

因此优先选择让处于job-cleanup taskjob1优先执行正是确保了这一点

同时job-cleanup task执行完后如果是mapjob-cleanup task, 就可以继续接着执行reduce. 

如果是reducejob-cleanup task, 那么作业就真正结束了而我们的目的正是让一个作业尽快地执行完


限制条件

jobs队列中的作业是在提交作业后直到作业完成从队列中移除只要作业没有完成就一直存在

因此一个作业必然经过job-setup > map > reduce > job-cleanup 这几个阶段

在如何选择以上任务的时候由于作业执行顺序的约定有一些条件是必须限制的

**1. 选择job-setup task:**

作业已经初始化完毕作业如果还没有初始化完毕不能开始执行job-setup --> `前置动作`

作业状态为PREP, 新创建JobInProgress状态初始为PREP --> `前置状态`

job-setup-task还没执行当前任务job-setup task还没执行才可以执行如果已经启动了就无需被选择了

作业未被kill, 作业未失败作业如果还没开始就被杀死或失败就无需执行这个作业的准备工作了

  /** Check whether setup task can be launched for the job.   * Setup task can be launched after the tasks are inited  and Job is in PREP state   * and if it is not already launched  or job is not Killed/Failed */  private synchronized boolean canLaunchSetupTask() {    return (tasksInited && status.getRunState() == JobStatus.PREP && !launchedSetup && !jobKilled && !jobFailed);  }


**2.选择job-cleanup task:** 

作业状态是PREPRUNNING  作业执行的顺序决定了作业的状态: PREP->RUNNING--[m/r]--[job-cleanup]->SUCCEEDED, 确保正确的`前置状态`

未启动job-cleanup job-setup已完成如果job-setup未完成就不允许选择job-cleanup

作业被kill或者作业失败作业被杀死或失败需要清理资源

作业所有的mapreduce任务都完成确保所有`前置动作`已完成作业所有的mapreduce都完成了(任务不管是否成功), 才可以执行job-cleanup

 /** Check whether cleanup task can be launched for the job.   * Cleanup task can be launched if it is not already launched 还没执行过   * or job is Killed 作业被杀死, 执行job-cleanup, 删除执行作业过程中产生的临时文件等   * or all maps and reduces are complete 所有的map/reduce任务都完成后, 可以执行job-cleanup */  private synchronized boolean canLaunchJobCleanupTask() {    // check if the job is running 作业状态必须是PREP或者RUNNING才可以执行job-cleanup    if (status.getRunState() != JobStatus.RUNNING && status.getRunState() != JobStatus.PREP) {      return false;    }    // check if cleanup task has been launched already or if setup isn't launched already.    // The later check is useful when number of maps is zero. 还没启动cleanup而且setup必须已经完成    if (launchedCleanup || !isSetupFinished()) {  // true: !lauchedCleanup && isSetupFinished      return false;    }    // check if job has failed or killed    if (jobKilled || jobFailed) return true;    // Check if all maps and reducers have finished.    boolean launchCleanupTask = ((finishedMapTasks + failedMapTIPs) == (numMapTasks));    if (launchCleanupTask) {  // 一个作业的所有map任务都完成了(尽管有些map失败了), 再判断所有reduce是否完成       launchCleanupTask = ((finishedReduceTasks + failedReduceTIPs) == numReduceTasks);  // 如果作业的所有reduce都完成了, 就可以执行job-cleanup    }    return launchCleanupTask;  }

数据来源

  List<TaskAttemptID> mapCleanupTasks = new LinkedList<TaskAttemptID>(); // A list of cleanup tasks for the map task attempts, to be launched  List<TaskAttemptID> reduceCleanupTasks = new LinkedList<TaskAttemptID>(); // A list of cleanup tasks for the reduce task attempts, to be launched  TaskInProgress maps[] = new TaskInProgress[0];  TaskInProgress reduces[] = new TaskInProgress[0];// upper 4 data structures are all for task-cleanup task [2]  TaskInProgress cleanup[] = new TaskInProgress[0];// for job-cleanup task [1]  TaskInProgress setup[] = new TaskInProgress[0];// for job-setup task    public synchronized void updateTaskStatus(TaskInProgress tip, TaskStatus status) {      if (state == TaskStatus.State.FAILED_UNCLEAN || state == TaskStatus.State.KILLED_UNCLEAN) {         tip.incompleteSubTask(taskid, this.status);        // add this task, to be rescheduled as cleanup attempt        if (tip.isMapTask()) { mapCleanupTasks.add(taskid);        } else { reduceCleanupTasks.add(taskid); }        // Remove the task entry from jobtracker        jobtracker.removeTaskEntry(taskid);      }  }


原创粉丝点击