MapReduce调度与执行原理之任务执行(一)

前言 :本文旨在理清在Hadoop中一个MapReduce作业(Job)在提交到框架后的整个生命周期过程,权作总结和日后参考,如有问题,请不吝赐教。本文不涉及Hadoop的架构设计,如有兴趣请参考相关书籍和文献。在梳 理过程中,我对一些感兴趣的源码也会逐行研究学习,以期强化基础。
作者 :Jaytalent
开始日期 :2013年9月9日
参考资料:【1】《Hadoop技术内幕--深入解析MapReduce架构设计与实现原理》董西成
                  【2】   Hadoop 1.0.0 源码
                            【3】《Hadoop技术内幕--深入解析Hadoop Common和HDFS架构设计与实现原理》蔡斌 陈湘萍
一个MapReduce作业的生命周期大体分为5个阶段 【1】
1. 作业提交与初始化
2. 任务调度与监控
3. 任务运行环境准备
4. 任务执行
5. 作业完成
这篇文章将任务的准备、执行到整个作业完成的过程进行研究。

一、任务启动

任务启动过程由TaskTracker完成。启动过程如下:首先判断是否是第一次收到某个作业的任务,若是就进行作业本地化,然后创建任务目录,否则直接创建任务目录。接下来启动JVM,并从TaskTracker获取任务,进行任务本地化并执行任务。任务执行后,若该JVM没有到重用次数上限,则再次从TaskTracker获取任务,重复上述过程。总体上讲,主要包括作业本地化、任务本地化和启动任务三步。本地化的目的是为任务运行创建一个环境,包括工作目录、下载运行所需文件和设置环境变量等。作业本地化由该作业第一个任务启动时执行。下面依次来看上述步骤。

1. 作业本地化

我们先从TaskTracker(以下简称TT)收到心跳响应说起。在TT的offerService方法中,通过transmitHeartbeat方法收到心跳响应,并从中提取出需要执行的命令:
HeartbeatResponse heartbeatResponse = transmitHeartBeat(now);
TaskTrackerAction[] actions = heartbeatResponse.getActions();
if (actions != null){ 
  for(TaskTrackerAction action: actions) {
    if (action instanceof LaunchTaskAction) {
      addToTaskQueue((LaunchTaskAction)action);
    } else if (action instanceof CommitTaskAction) {
      CommitTaskAction commitAction = (CommitTaskAction)action;
    }
  }
}
addToTaskQueue方法根据任务类型选择指定的启动器:
  private void addToTaskQueue(LaunchTaskAction action) {
    if (action.getTask().isMapTask()) {
      mapLauncher.addToTaskQueue(action);
    } else {
      reduceLauncher.addToTaskQueue(action);
    }
  }
其中mapLauncher和reduceLauncher是两个TaskLauncher对象,该对象继承于Thread类,即启动每个map或reduce任务各由一个单独的线程完成。
addToTaskQueue方法将任务注册后加入待启动任务列表,并通知等待线程有新任务加入。
    public void addToTaskQueue(LaunchTaskAction action) {
      synchronized (tasksToLaunch) {
        TaskInProgress tip = registerTask(action, this);
        tasksToLaunch.add(tip);
        tasksToLaunch.notifyAll();
      }
    }
在启动线程的run方法中调用startNewTask方法启动一个任务:
  void startNewTask(final TaskInProgress tip) throws InterruptedException {
    Thread launchThread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          RunningJob rjob = localizeJob(tip);
          tip.getTask().setJobFile(rjob.getLocalizedJobConf().toString());
          // Localization is done. Neither rjob.jobConf nor rjob.ugi can be null
          launchTaskForJob(tip, new JobConf(rjob.getJobConf()), rjob); 
        } catch (Throwable e) {
          String msg = ("Error initializing " + tip.getTask().getTaskID() + 
                        ":\n" + StringUtils.stringifyException(e));
          LOG.warn(msg);
          tip.reportDiagnosticInfo(msg);
        }
      }
    });
    launchThread.start();
  }
从上面实现看出,每个一个任务的具体启动过程是由一个单独的线程完成的。这种设计的考虑是:用户应用程序依赖的文件很大,从HDFS上下载的时间会很长,如果串行启动任务必然导致时间过长。
    synchronized (rjob) {
        if (!rjob.localized) {
          while (rjob.localizing) {
            rjob.wait();
          }
          if (!rjob.localized) {
            //this thread is localizing the job
            rjob.localizing = true;
          }
        }
      }
      if (!rjob.localized) {
        Path localJobConfPath = initializeJob(t, rjob, ttAddr);
      }
注意这段代码,每个作业有两个状态:localizing和localized,分别表示作业正在本地化和作业本地化已经完成。当第一个任务启动时,作业处于正在本地化的状态,作业不会等待,直接将localizing设置为true,并执行initializeJob执行作业
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值