MapReduce过程源码分析
MapReduce过程源码分析
Mapper
首先mapper完成映射,将word映射成(word,1)的形式。
MapReduce进程,Map阶段也叫MapTask,在MapTask中会通过run()方法来调用我们用户重写的mapper() 方法,
分布式的运算程序往往需要分成至少两个阶段:Map阶段和Reduce阶段。
第一个阶段,即Map阶段的maptask并发实例,完全并行独立运行,互不相干,如Map将要处理的多个文件的每个文件分成3份,分别放在集群中的各个数据节点,Map阶段中由maptask进程来处理已经存进来的文件,一行一行地去读数据,按空格切分行内单词,切分完毕之后,将单词统计出来以hashmap存储,其中以单词为key,以1作为单词的value。等到分配给自己的数据片全部读完之后,将这个hashmap按照首个字母的范围分成2个hashmap(分区排序),两个hashmap分别为:HashMap(a-p)和HashMap(q-z)。
第二个阶段的reduce task并发实例互不相干,但是他们的数据依赖于上一个阶段的所有maptask的并发实例的输出。
reduce task 分别统计a-p开头的单词和q-z开头的单词,然后输出结果到文件。
注意:MapReduce编程模型只能包含一个map阶段和一个reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行执行。
那么maptask如何进行任务分配?
reducetask如何进行任务分配?
maptask和reducetask之间如何衔接?
如果maptask运行失败,如何处理?
maptask如果都要自己负责输出数据的分区,很麻烦
MrAPPMaster负责整个程序的过程调度及状态的协调。
三个进程分别对应三个类:
三个进程:
1)MrAppMaster:负责整个程序的过程调度及状态协调
2)MapTask:负责map阶段的整个数据处理流程
3)ReduceTask:负责reduce阶段的整个数据处理流程
分别对应的类:
1)Driver阶段
整个程序需要一个Drvier来进行提交,提交的是一个描述了各种必要信息的job对象
2)Mapper阶段
(1)用户自定义的Mapper要继承自己的父类
(2)Mapper的输入数据是KV对的形式(KV的类型可自定义)
(3)Mapper中的业务逻辑写在map()方法中
(4)Mapper的输出数据是KV对的形式(KV的类型可自定义)
(5)map()方法(maptask进程)对每一个<K,V>调用一次
3)Reducer阶段
(1)用户自定义的Reducer要继承自己的父类
(2)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
(3)Reducer的业务逻辑写在reduce()方法中
(4)Reducetask进程对每一组相同k的<k,v>组调用一次reduce()方法
数据切片与MapTask并行度的决定机制
1.一个Job的Map阶段并行度由客户端在提交Job时的切片数决定
2.每一个Split切片分配一个MapTask并行实例处理
3.默认情况下,切片大小=BlockSize
4.切片时不靠路数据集整体,而是诸葛针对每一个文件单独切片。
job的提交过程分析
1 提交任务---->2 检查状态(if (state == JobState.DEFINE) {submit();}
)---->3.0 submit():3.1 确保job状态(ensureState(JobState.DEFINE)
)---->3.2 使用新的API(setUseNewAPI()
)---->3.3 连接集群(connect()
)---->3.4 根据get到的集群获取任务提交器(final JobSubmitter submitter = getJobSubmitter(cluster.getFileSystem(), cluster.getClient())
)---->3.5 submitter提交任务(return submitter.submitJobInternal(Job.this, cluster)
)----->3.5.1检查输出路径是否设置以及输出路径是否存在(checkSpecs(jobs)
)---->3.5.2注册JobId(JobID jobId = submitClient.getNewJobID();
)---->向目录拷贝一个文件,该文件就是我们之前(setJarByClass(xxx.class))设置好的jar包---->客户端就是通过该方法调用InputFormat来给我们的输入文件进行切片---->切片之后,执行conf.setInt(MRJobConfig.NUM_MAPS, maps);
将切片信息写入到目录(submitJobDir)中---->把job的配置信息conf也提交到submitDir---->任务正式提交。
首先我们调用:
boolean b = job.waitForCompletion(true);
该方法的主体:
public boolean waitForCompletion(boolean verbose
) throws IOException, InterruptedException,
ClassNotFoundException {
if (state == JobState.DEFINE) {
submit();
}
if (verbose) {
monitorAndPrintJob();
} else {
// get the completion poll interval from the client.
int completionPollIntervalMillis =
Job.getCompletionPollInterval(cluster.getConf());
while (!isComplete()) {
try {
Thread.sleep(completionPollIntervalMillis);
} catch (InterruptedException ie) {
}
}
}
return isSuccessful();
}
然后调用submit()方法:
/**
* Submit the job to the cluster and return immediately.
* @throws IOException
*/
public void submit()
throws IOException, InterruptedException, ClassNotFoundException {
ensureState(JobState.DEFINE);
setUseNewAPI();
connect();
final JobSubmitter submitter =
getJobSubmitter(cluster.getFileSystem(), cluster.getClient());
status = ugi.doAs(new PrivilegedExceptionAction<JobStatus>() {
public JobStatus run() throws IOException, InterruptedException,
ClassNotFoundException {
return submitter.submitJobInternal(Job.this, cluster);
}
});
state = JobState.RUNNING;
LOG.info("The url to track the job: " + getTrackingURL());
}
在submit()方法中,通过ensureState(JobState.DEFINE)
方法再次确认job的状态是否为DEFINE
,如果是,就设置使用新的API(hadoop2.x版本升级了很多新的API,而老的版本调用MapReduce程序的时候,在这里自动转成新的API),然后调用connect()
方法建立和yarn集群的连接。
connect()
方法的内容如下:
private synchronized void connect()
throws IOException, InterruptedException, ClassNotFoundException {
if (cluster == null) {
cluster =
ugi.doAs(new PrivilegedExceptionAction<Cluster>() {
public Cluster run()
throws IOException, InterruptedException,
ClassNotFoundException {
return new Cluster(getConfiguration());
}
});
}
}
在connect()
方法中,首先判断cluster,如果集群为null,那么就返回一个新的集群,return new Cluster(getConfiguration());
,如果任务的配置是本地模式就是一个LocalMaster,如果是yarn集群就是YarnMaster。
连接到cluster之后,然后就可以提交我们要执行的任务了,这时执行
final JobSubmitter submitter = getJobSubmitter(cluster.getFileSystem(), cluster.getClient());
通过返回的集群的客户端和协议来获得一个submitter,然后执行语句
return submitter.submitJobInternal(Job.this, cluster);
利用submitter来真正的提交我们的job任务,submitJobInternal(Job.this, cluster)
这个方法是真正提交job任务的方法,该方法的内容见文章最后。
然后通过checkSpecs(jobs)
方法检查输出路径是否设置以及输出路径是否存在,然后执行语句:
Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);
根据我们设置的cluster获取我们的stagingDir,即存放MapReduce过程中产生数据的临时文件夹。
然后执行JobID jobId = submitClient.getNewJobID();
对我们的job进行注册,进而得到JobId,通过job.setJobID(jobId);
设置我们注册好的jobId,然后执行copyAndConfigureFiles(job, submitJobDir);
向目录拷贝一个文件,该文件就是我们之前(setJarByClass(xxx.class))设置好的jar包,然后执行int maps = writeSplits(job, submitJobDir);
客户端就是通过该方法调用InputFormat来给我们的输入文件进行切片,切片之后,执行conf.setInt(MRJobConfig.NUM_MAPS, maps);
将切片信息写入到目录(submitJobDir)中。所谓切片信息就是标注了哪台机器处理哪个切片数据,相当于一个索引信息,有了这个索引信息,MapReduce的APPMaster在执行任务的时候就可以知道启动几个maptask并且知道每个机器处理哪一个部分的数据,所以这个信息也是要提交到HDFS的临时目录里面。
然后执行writeConf(conf, submitJobFile);
把我们的job的配置信息conf也提交到submitDir,执行完之后,临时目录中生成job.xml(存放job的配置信息,包括集群的各种手动配置及默认配置信息)。
其实到此为止,这一系列的工作都是在为集群的工作做准备,集群中创建一个临时目录,它是可以供集群中所有的数据节点进行访问的,首先在集群的临时目录中存放了jar包,然后放置了切片信息,最后又放置了配置文件,这样maptask可以到临时文件夹中读取存放的这三个信息,进而执行他们各自的任务。
然后程序第240行真正进行job的提交,然后任务开始运行。
至此程序执行完毕。
总结
任务的提交过程:首先是检查任务的状态,检查输出目录,都没问题之后,然后开始连接集群,因为任务都是交由集群中的其他人来执行,所以其他人需要得知这个任务的必要信息,因此job提交的时候有必要将这些任务的必要信息提交到大家都可以访问的临时目录(在HDFS上),这些必要的信息包括:jar包、切片信息以及配置信息(job.xml)。
附:submitJobInternal(Job.this, cluster)方法原代码:
/**
* Internal method for submitting jobs to the system.
*
* <p>The job submission process involves:
* <ol>
* <li>
* Checking the input and output specifications of the job.
* </li>
* <li>
* Computing the {@link InputSplit}s for the job.
* </li>
* <li>
* Setup the requisite accounting information for the
* {@link DistributedCache} of the job, if necessary.
* </li>
* <li>
* Copying the job's jar and configuration to the map-reduce system
* directory on the distributed file-system.
* </li>
* <li>
* Submitting the job to the <code>JobTracker</code> and optionally
* monitoring it's status.
* </li>
* </ol></p>
* @param job the configuration to submit
* @param cluster the handle to the Cluster
* @throws ClassNotFoundException
* @throws InterruptedException
* @throws IOException
*/
JobStatus submitJobInternal(Job job, Cluster cluster)
throws ClassNotFoundException, InterruptedException, IOException {
//validate the jobs output specs
checkSpecs(job);
Configuration conf = job.getConfiguration();
addMRFrameworkToDistributedCache(conf);
Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);
//configure the command line options correctly on the submitting dfs
InetAddress ip = InetAddress.getLocalHost();
if (ip != null) {
submitHostAddress = ip.getHostAddress();
submitHostName = ip.getHostName();
conf.set(MRJobConfig.JOB_SUBMITHOST,submitHostName);
conf.set(MRJobConfig.JOB_SUBMITHOSTADDR,submitHostAddress);
}
JobID jobId = submitClient.getNewJobID();
job.setJobID(jobId);
Path submitJobDir = new Path(jobStagingArea, jobId.toString());
JobStatus status = null;
try {
conf.set(MRJobConfig.USER_NAME,
UserGroupInformation.getCurrentUser().getShortUserName());
conf.set("hadoop.http.filter.initializers",
"org.apache.hadoop.yarn.server.webproxy.amfilter.AmFilterInitializer");
conf.set(MRJobConfig.MAPREDUCE_JOB_DIR, submitJobDir.toString());
LOG.debug("Configuring job " + jobId + " with " + submitJobDir
+ " as the submit dir");
// get delegation token for the dir
TokenCache.obtainTokensForNamenodes(job.getCredentials(),
new Path[] { submitJobDir }, conf);
populateTokenCache(conf, job.getCredentials());
// generate a secret to authenticate shuffle transfers
if (TokenCache.getShuffleSecretKey(job.getCredentials()) == null) {
KeyGenerator keyGen;
try {
keyGen = KeyGenerator.getInstance(SHUFFLE_KEYGEN_ALGORITHM);
keyGen.init(SHUFFLE_KEY_LENGTH);
} catch (NoSuchAlgorithmException e) {
throw new IOException("Error generating shuffle secret key", e);
}
SecretKey shuffleKey = keyGen.generateKey();
TokenCache.setShuffleSecretKey(shuffleKey.getEncoded(),
job.getCredentials());
}
if (CryptoUtils.isEncryptedSpillEnabled(conf)) {
conf.setInt(MRJobConfig.MR_AM_MAX_ATTEMPTS, 1);
LOG.warn("Max job attempts set to 1 since encrypted intermediate" +
"data spill is enabled");
}
copyAndConfigureFiles(job, submitJobDir);
Path submitJobFile = JobSubmissionFiles.getJobConfPath(submitJobDir);
// Create the splits for the job
LOG.debug("Creating splits at " + jtFs.makeQualified(submitJobDir));
int maps = writeSplits(job, submitJobDir);
conf.setInt(MRJobConfig.NUM_MAPS, maps);
LOG.info("number of splits:" + maps);
// write "queue admins of the queue to which job is being submitted"
// to job file.
String queue = conf.get(MRJobConfig.QUEUE_NAME,
JobConf.DEFAULT_QUEUE_NAME);
AccessControlList acl = submitClient.getQueueAdmins(queue);
conf.set(toFullPropertyName(queue,
QueueACL.ADMINISTER_JOBS.getAclName()), acl.getAclString());
// removing jobtoken referrals before copying the jobconf to HDFS
// as the tasks don't need this setting, actually they may break
// because of it if present as the referral will point to a
// different job.
TokenCache.cleanUpTokenReferral(conf);
if (conf.getBoolean(
MRJobConfig.JOB_TOKEN_TRACKING_IDS_ENABLED,
MRJobConfig.DEFAULT_JOB_TOKEN_TRACKING_IDS_ENABLED)) {
// Add HDFS tracking ids
ArrayList<String> trackingIds = new ArrayList<String>();
for (Token<? extends TokenIdentifier> t :
job.getCredentials().getAllTokens()) {
trackingIds.add(t.decodeIdentifier().getTrackingId());
}
conf.setStrings(MRJobConfig.JOB_TOKEN_TRACKING_IDS,
trackingIds.toArray(new String[trackingIds.size()]));
}
// Set reservation info if it exists
ReservationId reservationId = job.getReservationId();
if (reservationId != null) {
conf.set(MRJobConfig.RESERVATION_ID, reservationId.toString());
}
// Write job file to submit dir
writeConf(conf, submitJobFile);
//
// Now, actually submit the job (using the submit name)
//
printTokens(jobId, job.getCredentials());
status = submitClient.submitJob(
jobId, submitJobDir.toString(), job.getCredentials());
if (status != null) {
return status;
} else {
throw new IOException("Could not launch job");
}
} finally {
if (status == null) {
LOG.info("Cleaning up the staging area " + submitJobDir);
if (jtFs != null && submitJobDir != null)
jtFs.delete(submitJobDir, true);
}
}
}
MapReduce过程源码分析的更多相关文章
- [Android]从Launcher开始启动App流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...
- [Android]Android系统启动流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...
- Android系统默认Home应用程序(Launcher)的启动过程源码分析
在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...
- Android Content Provider的启动过程源码分析
本文參考Android应用程序组件Content Provider的启动过程源码分析http://blog.csdn.net/luoshengyang/article/details/6963418和 ...
- Android应用程序绑定服务(bindService)的过程源码分析
Android应用程序组件Service与Activity一样,既能够在新的进程中启动,也能够在应用程序进程内部启动:前面我们已经分析了在新的进程中启动Service的过程,本文将要介绍在应用程序内部 ...
- Spring加载流程源码分析03【refresh】
前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...
- 【高速接口-RapidIO】5、Xilinx RapidIO核例子工程源码分析
提示:本文的所有图片如果不清晰,请在浏览器的新建标签中打开或保存到本地打开 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:Vivado2015.4.2 ...
- 转:Spring与Mybatis整合的MapperScannerConfigurer处理过程源码分析
原文地址:Spring与Mybatis整合的MapperScannerConfigurer处理过程源码分析 前言 本文将分析mybatis与spring整合的MapperScannerConfigur ...
- 5.Xilinx RapidIO核例子工程源码分析
https://www.cnblogs.com/liujinggang/p/10091216.html 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:V ...
随机推荐
- C#中RDLC控制某列的显示隐藏
使用 1.添加参数IsEnable(用于控制显示或隐藏)2.在RDLC页面,需要控制的列上,右键,列的可见性...,基于表达式隐藏或显示3.输入 IIF(Parameters!IsEnable.Val ...
- Multipass使用教程
一.Multipass介绍 Multipass是一种简单的虚拟机工具.它不仅使启用虚拟机变得快速简易,还使管理那些虚拟机变得异常简单,因此可以立即开始针对云.边缘.物联网或任何一种类型的技术进行开发. ...
- 最全的Visual Studio Code配置及使用教程
史上最全vscode配置使用教程 工欲善其事,必先利其器.想要优雅且高效的编写代码,必须熟练使用一款前端开发工具.但前端开发工具数不胜数,像HBuilder.Sublime Text.WebStorm ...
- 一文掌握XSS
目录 XSS跨站脚本攻击 1.什么叫跨站脚本攻击? 2.XSS跨站脚本攻击的原理 3.XSS跨站脚本攻击的目的是什么? 4.XSS跨站脚本攻击出现的原因 5.XSS跨站脚本攻击的条件 1.有输入有输出 ...
- GC算法与回收策略
算法: 标记-清理 :首先标记出需要回收的对象 ,然后统一回收待标记的对象. 缺点:易产生大量空间碎片,空间碎片太多导致程序在运行过程中产生大对象时 因为空间不足导致容易导致另一个垃圾收集动作 标记 ...
- 基于http的netty demo
1.引入netty的pom <dependency> <groupId>io.netty</groupId> <artifactId>netty-all ...
- ROS开源小车TurtleBot3详情介绍
您为什么要选择ROS开源智能小车 ROS(Robot Operating System,机器人操作系统)是目前世界上更主流更多人使用的的机器人开源操作系统.它可以提供操作系统应有的服务,包括硬件抽象, ...
- Java异常体系概述
Java的异常体系结构 Java异常体系的根类是 Throwable, 所以当写在java代码中写throw抛出异常时,后面跟的对象必然是Throwable或其子类的对象. 其中Exception异常 ...
- 在vscode中配置sass savepath
1.先在VSCode上面安装插件:Live Sass Compiler 2.创建好scss文件夹文件和css文件夹 3.然后在VSCode的控制台上打开Live sass watching模式(控制台 ...
- nacos服务注册与发现原理解析
前言:nacos 玩过微服务的想必不会陌生,它是阿里对于springcloud孵化出来的产品,用来完成服务之间的注册发现和配置中心,其核心作用我就不废话了 大致流程:每个服务都会有一个nacos cl ...