Hadoop随笔(一):工作流程的源码
一、几个可能会用到的属性值
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随笔(一):工作流程的源码的更多相关文章
- springmvc工作原理以及源码分析(基于spring3.1.0)
springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...
- Android 全面插件化 RePlugin 流程与源码解析
转自 Android 全面插件化 RePlugin 流程与源码解析 RePlugin,360开源的全面插件化框架,按照官网说的,其目的是“尽可能多的让模块变成插件”,并在很稳定的前提下,尽可能像开发普 ...
- Spark Streaming运行流程及源码解析(一)
本系列主要描述Spark Streaming的运行流程,然后对每个流程的源码分别进行解析 之前总听同事说Spark源码有多么棒,咱也不知道,就是疯狂点头.今天也来撸一下Spark源码. 对Spark的 ...
- SpringMVC执行流程及源码分析
SpringMVC流程及源码分析 前言 学了一遍SpringMVC以后,想着做一个总结,复习一下.复习写下面的总结的时候才发现,其实自己学的并不彻底.牢固.也没有学全,视频跟书本是要结合起来一起, ...
- HDFS追本溯源:HDFS操作的逻辑流程与源码解析
本文主要介绍5个典型的HDFS流程,这些流程充分体现了HDFS实体间IPC接口和stream接口之间的配合. 1. Client和NN Client到NN有大量的元数据操作,比如修改文件名,在给定目录 ...
- Android应用层View绘制流程与源码分析
1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...
- Struts流程分析+源码分析
1.初始化工作 读取配置---转换器-----读取插件 当struts-config.xml配置文件加载到内存,则会创建两个map:ActionConfigs,FromBeans.这两个map都交由M ...
- SpringMvc请求处理流程与源码探秘
流程梳理 dispatcherServlet作为前端控制器的主要作用就是接受请求与处理响应. 不过它不是传统意义上的servlet,它在接受到请求后采用转发的方式,将具体工作交给专业人士去做. 参与角 ...
- 20170908工作日记--Volley源码详解
Volley没有jar包,需要从官网上下载源码自己编译出来,或者做成相关moudle引入项目中.我们先从最简单的使用方法入手进行分析: //创建一个网络请求队列 RequestQueue reques ...
随机推荐
- [Linux] VIM 常用快捷键2
如何使用MacVim 1.在插入模式之外 基本上来说,你应该尽可能少的呆在插入模式里面,因为在插入模式里面 VIM 就像一个“哑巴”编辑器一样.很多新手都会一直呆在插入模式里面,因为这样易于使用.但 ...
- 【Unity】常用代码
//父子节点相关的: parent 变量表示Transform的父节点 root 表示它的根节点,如果没有父节点,它会返回自己 //根据名字查找子节点 Transform Find(string na ...
- gdb调试正执行的程序
参考,转载:http://biancheng.dnbcw.info/linux/391846.html
- XML与JSON的转换
-(void)test { //XML文本范例 NSString *testXMLString = @"Cake0.55RegularChocolateBluebe ...
- Tomcat 配置 默认应用 (去掉项目名称、移除项目名称)
Tomcat 配置默认应用,即只输入域名或ip,不用输入项目名称: <Host name="localhost" appBase="webapps" un ...
- 系统收到了多个不同的 Content-Disposition 标头。为了避免遭到 HTTP 响应拆分攻击,这种情况是不允许的。
今天使用Struts2进行上传下载的时候发现了一个现象 我的Struts2配置文件 <action name="share_ExportExcel" class=" ...
- Android ListView简单实用
layout创建: activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/ ...
- zoj3228Searching the String(ac自动机)
链接 这个题把病毒分为了两种,一种包含可以覆盖,另一种不可以,需要分别求出包含他们的个数,可以把两种都建在一颗tire树上,在最后求得时候判断一下当前节点是属于哪种字符串,如果是不包含的需要判断一下p ...
- STM32学习笔记(八) SPI总线(操作外部flash)
1. SPI总线简介 SPI全称串行外设接口,是一种高速,全双工,同步的外设总线:它工作在主从方式,常规需要至少4根线才能够正常工作.SPI作为基本的外设接口,在FLASH,EPPROM和一些数字通讯 ...
- sizeof既是关键字,又是运算符(操作符),但不是函数!
sizeof是关键字吗 sizeof是关键字,这一点毋庸置疑.你不能将sizeof定义为任何标识符.查看C语言标准文档里的说明: sizeof是运算符(操作符)吗 C语言中,sizeof是运算符(操作 ...