一、几个可能会用到的属性值

  1、mapred.map.tasks.speculative.execution和mapred.reduce.tasks.speculative.execution

  这两个属性可以决定Map任务和Reduce任务是否开启推测式执行策略。推测式执行策略在Hadoop中用来应对执行缓慢的任务所造成的瓶颈,但是对代码缺陷所导致的任务执行过慢,推测执行是一种反向的作用,应当避免,而Hadoop默认是开启推测式执行的。

  2、mapred.job.reuse.jvm.num.tasks

  这个属性值默认为1,表示一个JVM上只运行一个任务。如果Task Tracker具有很多小任务,重复启动JVM是非常耗时的,可以考虑任务JVM重用。将属性值设置为大于1的值表示启用了JVM;设置为-1表示共享此JVM的任务数目不受限制。尽管启用了JVM重用,但是Task Tracker上的多个任务还是顺序执行的,只是不需要额外启动JVM而已。

  3、Task Tracker的本地参数值

名称 类型 描述
mapred.job.id String job id
mapred.jar String job目录下job.jar的位置
job.local.dir String job指定的共享存储空间
mapred.tip.id String task id
mapred.task.id String task尝试id
mapred.task.partition int task在job中的id
map.input.file String map读取的文件名
map.input.start long map输入的数据块的起始位置的偏移
map.input.length long map输入的数据块的字节数
mapred.work.output.dir String task临时输出目录

 二、Hadoop工作流程

  以下Hadoop源码来源于hadoop-1.2.1。

  1、作业的提交

  hadoop项目的第一步自然是根据需求进行代码编写,而后是需要配置Map类、Reduce类、Input路径、Output路径以及其他的虚拟机配置等。但这些操作只是为hadoop形成了一个作业,还算不上Hadoop的工作流程,将项目提交执行后,hadoop进入完全自动的运行方式,工作流程才开始启动。

  作业的提交代码实现于JobClient类(JobClient.java)的submitJobInternal方法,分为以下几个步骤:

  a.获取Job的ID

    JobID jobId = jobSubmitClient.getNewJobId();

  b.分配job在HDFS中的资源空间,配置其路径

    Path submitJobDir = new Path(jobStagingArea, jobId.toString());
    jobCopy.set("mapreduce.job.dir", submitJobDir.toString());    //jobCopy是拷贝的一个job副本
    JobStatus status = null;

  c.监控地址令牌,直到可以对该空间进行操作。

    TokenCache.obtainTokensForNamenodes(jobCopy.getCredentials(),new Path [] {submitJobDir},jobCopy);
Path submitJobFile = JobSubmissionFiles.getJobConfPath(submitJobDir);
int reduces = jobCopy.getNumReduceTasks();
InetAddress ip = InetAddress.getLocalHost();
if (ip != null) {
job.setJobSubmitHostAddress(ip.getHostAddress());
job.setJobSubmitHostName(ip.getHostName());
}
JobContext context = new JobContext(jobCopy, jobId);

  d.检查output类型

    if (reduces == 0 ? jobCopy.getUseNewMapper() :
jobCopy.getUseNewReducer()) {
org.apache.hadoop.mapreduce.OutputFormat<?,?> output =
ReflectionUtils.newInstance(context.getOutputFormatClass(),
jobCopy);
output.checkOutputSpecs(context);
} else {
jobCopy.getOutputFormat().checkOutputSpecs(fs, jobCopy);
}

  e.为job划分splits

    FileSystem fs = submitJobDir.getFileSystem(jobCopy);
LOG.debug("Creating splits at " + fs.makeQualified(submitJobDir));
int maps = writeSplits(context, submitJobDir);
jobCopy.setNumMapTasks(maps);

  f.在job的配置文件中写入相关的排队、配置信息

    String queue = jobCopy.getQueueName();
AccessControlList acl = jobSubmitClient.getQueueAdmins(queue);
jobCopy.set(QueueManager.toFullPropertyName(queue,QueueACL.ADMINISTER_JOBS.getAclName()), acl.getACLString());

  g.将job的信息通知给JobTrack

    FSDataOutputStream out = FileSystem.create(fs, submitJobFile,new FsPermission(JobSubmissionFiles.JOB_FILE_PERMISSION));

  f.将job写入到HDFS中。需要注意的是,写入job之前要先将cache中的数据清除。这样做的原因:cache中的数据对当前job是没有用的了,如果在job写入之前清除是没有影响的;但是,如果在写入之后清除,cache中的很可能已经被更改为其他job的信息了,清除cache中的数据会破坏其他的job。

    TokenCache.cleanUpTokenReferral(jobCopy);
try {
jobCopy.writeXml(out);
} finally {
out.close();
}

  g.调用submitJob类来真正的提交job

    printTokens(jobId, jobCopy.getCredentials());
    status = jobSubmitClient.submitJob(jobId, submitJobDir.toString(), jobCopy.getCredentials());
    JobProfile prof = jobSubmitClient.getJobProfile(jobId);
    if (status != null && prof != null) {
return new NetworkedJob(status, prof, jobSubmitClient);
} else {
throw new IOException("Could not launch job");
}

  至此,一个作业就被完整地提交给JobTrack和HDFS了。

  2、作业的初始化

  作业的初始化主要是考虑到map和reduce都需要进行初始化,因此需要注意其中的细节。

  作业的初始化源码实现于JobInprogress类(JobInProgress.java)的initTask方法。作业初始化的步骤如下:

  a.首先需要获取job的一些配置信息和运行信息

    if (!jobtracker.getConf().getBoolean(JT_JOB_INIT_EXCEPTION_OVERRIDE, false) && getJobConf().getBoolean(JOB_INIT_EXCEPTION, false)) {
   waitForInitWaitLockForTests();
  }
    if (tasksInited || isComplete()) {
      return;
    }
    synchronized(jobInitKillStatus){    //互锁操作
      if(jobInitKillStatus.killed || jobInitKillStatus.initStarted) {
        return;
    }
    jobInitKillStatus.initStarted = true;
    }

  b.获取job的执行信息和优先级信息

  c.根据input splits的数目决定map的数目,每一个splits都需要一个map

    TaskSplitMetaInfo[] splits = createSplits(jobId);
    if (numMapTasks != splits.length) {
    throw new IOException("Number of maps in JobConf doesn't match number of " +
"recieved splits for job " + jobId + "! " +
"numMapTasks=" + numMapTasks + ", #splits=" + splits.length);
    }
    numMapTasks = splits.length;

  d.检查splits的位置,保证创建的map/reduce或者初始化的map/reduce都是有意义的,同时还需要在监控进程中设置map和reduce的信息

    for (TaskSplitMetaInfo split : splits) {
      NetUtils.verifyHostnames(split.getLocations());
    }
    jobtracker.getInstrumentation().addWaitingMaps(getJobID(), numMapTasks);
    jobtracker.getInstrumentation().addWaitingReduces(getJobID(), numReduceTasks);
    this.queueMetrics.addWaitingMaps(getJobID(), numMapTasks);
    this.queueMetrics.addWaitingReduces(getJobID(), numReduceTasks);

  e.为splits分配map

    maps = new TaskInProgress[numMapTasks];
    for(int i=0; i < numMapTasks; ++i) {
      inputLength += splits[i].getInputDataLength();
      maps[i] = new TaskInProgress(jobId, jobFile,
splits[i],
jobtracker, conf, this, i, numSlotsPerMap);
    }
    LOG.info("Input size for job " + jobId + " = " + inputLength+ ". Number of splits = " + splits.length);

  f.将map放入到等待执行的缓冲区内

    localityWaitFactor = conf.getFloat(LOCALITY_WAIT_FACTOR, DEFAULT_LOCALITY_WAIT_FACTOR);
    if (numMapTasks > 0) {
      nonRunningMapCache = createCache(splits, maxLevel);
    }
    // set the launch time
    this.launchTime = jobtracker.getClock().getTime();

  g.同样的对待reduce操作

  h.创建两个清除进程,一个清除map,一个清除reduce

    cleanup = new TaskInProgress[2];
    // cleanup map tip. This map doesn't use any splits. Just assign an empty
    // split.
    TaskSplitMetaInfo emptySplit = JobSplit.EMPTY_TASK_SPLIT;
    cleanup[0] = new TaskInProgress(jobId, jobFile, emptySplit, jobtracker, conf, this, numMapTasks, 1);
    cleanup[0].setJobCleanupTask();
    // cleanup reduce tip.
    cleanup[1] = new TaskInProgress(jobId, jobFile, numMapTasks,
numReduceTasks, jobtracker, conf, this, 1);
    cleanup[1].setJobCleanupTask();

  i.创建两个设置进程,分别对map和reduce进行初始化设置

    setup = new TaskInProgress[2];
    // setup map tip. This map doesn't use any split. Just assign an empty
    // split.
    setup[0] = new TaskInProgress(jobId, jobFile, emptySplit, jobtracker, conf, this, numMapTasks + 1, 1);
    setup[0].setJobSetupTask();
     // setup reduce tip.
    setup[1] = new TaskInProgress(jobId, jobFile, numMapTasks,
numReduceTasks + 1, jobtracker, conf, this, 1);
    setup[1].setJobSetupTask();

  j.最后,设置一个互锁的方法,用来检查初始化是否成功,并写入日志

    synchronized(jobInitKillStatus){
      jobInitKillStatus.initDone = true;
       // set this before the throw to make sure cleanup works properly
      tasksInited = true;
      if(jobInitKillStatus.killed) {
        throw new KillInterruptedException("Job " + jobId + " killed in init");
      }
    }
    JobHistory.JobInfo.logInited(profile.getJobID(), this.launchTime, numMapTasks, numReduceTasks);

  至此,作业的初始化操作就已经完成了。由于初始化操作需要根据splits数目来确定需要初始化的map和reduce操作,因此相比较有点复杂。

  3、作业的分配

  作业的分配需要JobTrack和TaskTrack相互协调,JobTrack根据TaskTrack发送的心跳信息来决定任务的分配方法。心跳信息会报告当前任务的状况,会提出新的任务请求,或者提出任务执行失败的报告等。JobTrack接受到信息后会采取相应的措施。

  Hadoop的作业分配是一种“拉”的方式,TaskTrack发送心跳信息(源码实现为TaskTrack类(TaskTrack.java)的transmitHeartBeat方法)给JobTrack来请求一个新的任务,而这个TaskTrack提出任务请求时也会提供自己当前map/reduce任务槽的数量供JobTrack参考。JobTrack根据心跳信息(源码实现为JobTrack类(JobTrack.java)的heartbeat方法)在本地优先的情况下对该TaskTrack分配Task。

  4、作业的执行

  作业的执行同样在TaskTrack中完成, 在作业执行时,第一步是将任务进行本地化,第二步是通过虚拟机执行任务。

  任务本地化,基于TaskTrack.java的localizeJob方法实现,主要步骤为:

  a.将job.split复制到本地;b.将job.jar复制到本地;c.将job的配置信息写入job.xml;d.创建本地目录,解压缩job.jar;e.调用launchTaskForJob方法发布任务;f.调用launchTask方法启动任务。

  任务的执行包括map执行(MapTaskRunner类)和reduce执行(ReduceTaskRunner类),每个任务的执行都是通过一个JVM实现的。

  5、作业的进度

  作业进度的更新依赖于TaskTrack向JobTrack发送的心跳信息。每个TaskTrack都会将自己的进度信息和状态信息封装在心跳信息中,每隔5秒向JobTrack发送一次。JobTrack在收集所有的信息后统一并得出全局信息。

  

Hadoop随笔(一):工作流程的源码的更多相关文章

  1. springmvc工作原理以及源码分析(基于spring3.1.0)

    springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...

  2. Android 全面插件化 RePlugin 流程与源码解析

    转自 Android 全面插件化 RePlugin 流程与源码解析 RePlugin,360开源的全面插件化框架,按照官网说的,其目的是“尽可能多的让模块变成插件”,并在很稳定的前提下,尽可能像开发普 ...

  3. Spark Streaming运行流程及源码解析(一)

    本系列主要描述Spark Streaming的运行流程,然后对每个流程的源码分别进行解析 之前总听同事说Spark源码有多么棒,咱也不知道,就是疯狂点头.今天也来撸一下Spark源码. 对Spark的 ...

  4. SpringMVC执行流程及源码分析

    SpringMVC流程及源码分析 前言 ​ 学了一遍SpringMVC以后,想着做一个总结,复习一下.复习写下面的总结的时候才发现,其实自己学的并不彻底.牢固.也没有学全,视频跟书本是要结合起来一起, ...

  5. HDFS追本溯源:HDFS操作的逻辑流程与源码解析

    本文主要介绍5个典型的HDFS流程,这些流程充分体现了HDFS实体间IPC接口和stream接口之间的配合. 1. Client和NN Client到NN有大量的元数据操作,比如修改文件名,在给定目录 ...

  6. Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  7. Struts流程分析+源码分析

    1.初始化工作 读取配置---转换器-----读取插件 当struts-config.xml配置文件加载到内存,则会创建两个map:ActionConfigs,FromBeans.这两个map都交由M ...

  8. SpringMvc请求处理流程与源码探秘

    流程梳理 dispatcherServlet作为前端控制器的主要作用就是接受请求与处理响应. 不过它不是传统意义上的servlet,它在接受到请求后采用转发的方式,将具体工作交给专业人士去做. 参与角 ...

  9. 20170908工作日记--Volley源码详解

    Volley没有jar包,需要从官网上下载源码自己编译出来,或者做成相关moudle引入项目中.我们先从最简单的使用方法入手进行分析: //创建一个网络请求队列 RequestQueue reques ...

随机推荐

  1. [Linux] VIM 常用快捷键2

    如何使用MacVim 1.在插入模式之外 基本上来说,你应该尽可能少的呆在插入模式里面,因为在插入模式里面 VIM 就像一个“哑巴”编辑器一样.很多新手都会一直呆在插入模式里面,因为这样易于使用.但 ...

  2. 【Unity】常用代码

    //父子节点相关的: parent 变量表示Transform的父节点 root 表示它的根节点,如果没有父节点,它会返回自己 //根据名字查找子节点 Transform Find(string na ...

  3. gdb调试正执行的程序

    参考,转载:http://biancheng.dnbcw.info/linux/391846.html

  4. XML与JSON的转换

    -(void)test {          //XML文本范例     NSString *testXMLString = @"Cake0.55RegularChocolateBluebe ...

  5. Tomcat 配置 默认应用 (去掉项目名称、移除项目名称)

    Tomcat 配置默认应用,即只输入域名或ip,不用输入项目名称: <Host name="localhost" appBase="webapps" un ...

  6. 系统收到了多个不同的 Content-Disposition 标头。为了避免遭到 HTTP 响应拆分攻击,这种情况是不允许的。

    今天使用Struts2进行上传下载的时候发现了一个现象 我的Struts2配置文件 <action name="share_ExportExcel" class=" ...

  7. Android ListView简单实用

    layout创建: activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/ ...

  8. zoj3228Searching the String(ac自动机)

    链接 这个题把病毒分为了两种,一种包含可以覆盖,另一种不可以,需要分别求出包含他们的个数,可以把两种都建在一颗tire树上,在最后求得时候判断一下当前节点是属于哪种字符串,如果是不包含的需要判断一下p ...

  9. STM32学习笔记(八) SPI总线(操作外部flash)

    1. SPI总线简介 SPI全称串行外设接口,是一种高速,全双工,同步的外设总线:它工作在主从方式,常规需要至少4根线才能够正常工作.SPI作为基本的外设接口,在FLASH,EPPROM和一些数字通讯 ...

  10. sizeof既是关键字,又是运算符(操作符),但不是函数!

    sizeof是关键字吗 sizeof是关键字,这一点毋庸置疑.你不能将sizeof定义为任何标识符.查看C语言标准文档里的说明: sizeof是运算符(操作符)吗 C语言中,sizeof是运算符(操作 ...