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

  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. Oracle 流复制实践笔记

    最近因为业务需求,需要在两个数据库之间做双向实时同步,遂实践了一把Oracle的流复制,遇到了很多疑难问题,最终也貌似成功,现记录如下. 我是使用OEM来实现流复制的. 10.进行流复制的两个数据库的 ...

  2. lua 操作中文字符串之截取和长度竖排显示

    前言 在游戏中,我们经常会遇到汉字的多行显示,比如名字竖行显示等.如下图: 为了实现上面的效果,lua实现分行是通过  \n  实现的,所以我们需要取出汉字,然后插入 \n 实现分行效果.还有一种就是 ...

  3. cf 700 A As Fast As Possible

    题意:有$n$个小学生需要到距离为$l$的地方去,步行的速度是$v_1$,它们租了一辆大巴,速度是$v_2$,大巴上最多容纳$k$个乘客,每个小学生最多乘车一次,初始时大巴和小学生都在起点,问至少需要 ...

  4. 【Linux命令与工具】磁盘与目录的容量——df和du

    df(disk free):列出文件系统的整体磁盘使用量 用法: df [-akmhi] [目录或文件名] 参数: -a: 列出所有的文件系统,包括系统特有的/proc等文件系统 -k: 以KB的容量 ...

  5. easylui datagrid 动态生成列

    function load(sdate) { $.getJSON("workorder/statistics.do", { sdate : sdate+'-01' }, funct ...

  6. JDBC读取新插入Oracle数据库Sequence值的5种方法

    Oracle的sequence实现非常灵活,所以也带来一些易用性问题,如何取到新插入记录生成的sequence值与其它数据库有较大差别,本文详国介绍了5种实现读取新插入记录sequence值的方法. ...

  7. Mysql新知识点150928

    1.select distinct(DATE_FORMAT(updatetime,'%Y-%m')) as updatetime from barcode where pid!=0 order by ...

  8. hdu 4283 You Are the One 区间dp

    You Are the One Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  9. PHP-----函数和二进制

    递归-----函数本身调用本身.每一个栈中的变量都是独立的,不受外部变量的影响,除非传参.这一点和Js不一样. 在一个php页面中要引用其他的php文件可以使用require,require_once ...

  10. 【OOAD】OOAD概述

    什么是面向对象? OOP:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构.OOP 的一条基本原则是计算机程序是由单个能够起到子程序 ...