【Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程】第三节 Job提交前的初始化

【Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程】第三节 Job提交前的初始化

回顾

上一节中我们对 jobSubmitter(提交器对象)的初始化过程进行了跟踪,查看了相关初始化的内容,下面进入submitJobInternal(Job job, Cluster cluster) 方法中查看cluster与yarn的一些交互过程。

Job提交前的初始化

将提交器构造出来以后,接着使用构造器提交作业:
在这里插入图片描述
进入submitJobInternal(Job job, Cluster cluster)方法,checkSpecs(job)方法是用来检查我们的conf里的一些参数规格正确性(如何reduces数目是否为空,缓存是否开启等):
在这里插入图片描述
然后获取一个conf对象,addMRFrameworkToDistributedCache(conf)是将MapReduce框架添加到分布式缓存中,JobSubmissionFiles.getStagingDir(cluster, conf)方法是向申请了一个存放规划文档的地址(/user/deploy_man/.staging):
在这里插入图片描述
接着是获取客户端本机ip及主机名,并将其赋值给mapreduce.job.submithostname和mapreduce.job.submithostaddress:
在这里插入图片描述
接着获取一个jobId,该jobId将伴随着作业的整个生命历程,出现在WebUi和History等地方(它可视作为作业的唯一标识):
在这里插入图片描述
接着将之前申请的存放规划文档的地址(/user/deploy_man/.staging)+jobId拼接成一个新的路径,这就是存放我们作业相关文档的地方了:
在这里插入图片描述
我们先去hdfs上的/user/deploy_man/.staging/job_1577273840515_134821下看一下:
在这里插入图片描述
呃呃呃,没有搜到,看来还没有建立,我们继续向下,接着是将之前我们获取的一些参数值初始化为conf的一些属性(mapreduce.job.user.name、hadoop.http.filter.initializers、mapreduce.job.dir):
在这里插入图片描述
TokenCache.obtainTokensForNamenodes(job.getCredentials(), new Path[]{submitJobDir}, conf)是获得路径的授权令牌,简单来说就是获取在该路径下进行一些操作的授权令牌,this.populateTokenCache(conf, job.getCredentials())是将NameNode令牌缓存TokenCache中(其实还是一些参数的设置):
在这里插入图片描述
下一步,是生成一个key,关于该key的作用,我的猜测是验证洗牌(Shuffle)期间发生数据交换时数据的一致性:
在这里插入图片描述
向下,this.copyAndConfigureFiles(job, submitJobDir)就是将我们的主程序jar(BigData-1.0.jar)copy到之前分配的路径下(/user/deploy_man/.staging/job_1577273840515_134821):
在这里插入图片描述
去该路径下看一看,果然已经上传:
在这里插入图片描述
接着获取作业相关的文档(规划文档)的长传路径(和主程序jar的上传位置是一致的,因为其就是用submitJobDir获取的):
在这里插入图片描述
根据文件的split情况确定MapTask的数目:
在这里插入图片描述
进入writeSplits(JobContext job, Path jobSubmitDir)方法:
在这里插入图片描述
可以看到,它就是根据数据源的一些分布情况,根据我们设定的最大、最小的切片限制生成了作业规划(每块分成几块切片,每块切片从几行到几行等),那么由于我们的测试数据集实在是太小了,还不足以达到最小split切片限制,所以它最后获取的maps数目为0,这个时候我们可以去看看切片信息( job.split)和切片元数据信息( job.splitmetainfo):
在这里插入图片描述
好吧,可能是我的测试数据太小太小的缘故,所以规划信息也少的可怜,根本看不出来什么(因为被序列化了):
在这里插入图片描述
那继续往下,将获取到的maps(0个)数量写入到conf中后,获取maxMaps数量(默认-1),验证maxMaps在大于零的情况下,maps是否小于maxMaps(肯定是不成立的):
在这里插入图片描述
下一步获取job队列名(默认为default,我们这里获取到的就是默认值),然后根据job队列名,获取队列中访问控制列表(Access Control List :访问控制列表, 是经常使用流量控制工具,常用来控制流量和匹配感兴趣流),最后将mapred.queue.default. acl-administer-jobs的值设为允许所有用户(*即代表所有用户都能查看任务详情、改动任务优先级或者是杀掉任务),删除jobtoken引用(清空缓存的令牌):
在这里插入图片描述
接着判断是否需要追踪令牌ID(mapreduce.job.token.tracking.ids.enabled参数未配置默认为false,就是不追踪):
在这里插入图片描述
如果预留ID存在,则将其设置为mapreduce.job.reservation.id的值(这里是不存在的):在这里插入图片描述
下一步,this.writeConf(conf, submitJobFile)是将job对象序列化为一个xml文件,并提交到资源路径中(/user/deploy_man/.staging/job_1577273840515_134821):
在这里插入图片描述
我们查看该路径下果然多了一个xml文件:
在这里插入图片描述
查看job.xml内容,可以看到都是一些规则的标签记录:
在这里插入图片描述
this.writeConf(conf, submitJobFile)是打印jobId和token信息,接着正式提交作业(资源提交至相关路径完毕,向ResourceManager申请运作业),接收返回的状态,判断状态是否为空,为空则直接抛异常:
在这里插入图片描述
之后我们的任务就被提交至yarn ResourceManager队列中,等待被NameManager领取并去/user/deploy_man/.staging/job_1577273840515_134821路径下拿到配置文件,然后开启MapReduceApplication Master,开启MapReduceApplication Master之后,MapReduceApplication Master再向ResourceManager申请开启MapTask……
贴一下运行明细图(0个map和100个reduce):
在这里插入图片描述
生成100个文件(多少个ReduceTask就有多少个文件,这里多出来的一个是_SUCCESS空文件):
在这里插入图片描述

后记

关于Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程到这里就结束了,整过过程不算复杂,主要是一些配置的初始化过程比较绕,也由此真心佩服设计与编写这个架构的大牛们,是如何做到这般有条不紊的!

跳转

【Debug阅读Hadoop3.0.0源码之MapReduce Job提交流程】第一节 Configuration和Job对象的初始化
【Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程】第二节 jobSubmitter(提交器对象)的初始化
【Debug跟踪Hadoop3.0.0源码之MapReduce Job提交流程】第三节 Job提交前的初始化

  • 41
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MapReduce是一种用于处理大规模数据集的编程模型和软件框架。Hadoop是一个基于MapReduce模型的分布式文件存储和处理系统。在Hadoop中,MapReduce被广泛用于数据处理和分析任务。 自定义二次排序是MapReduce中常见的一种需求,其目的是对MapReduce的输出进行排序。下面我们来介绍一下如何在Linux上使用Hadoop实现自定义二次排序。 1. 准备数据 首先我们需要准备一个数据集,假设我们有一个文本文件,每行包含两个字段,分别为学生姓名和成绩,中间用制表符分隔。例如: ``` Tom 80 Jerry 70 Mike 90 Lucy 85 ``` 2. 编写Mapper代码 自定义二次排序需要进行两次排序,第一次按照学生姓名进行排序,第二次按照成绩进行排序。因此,我们需要在Mapper中将学生姓名和成绩作为Key-Value输出。 我们可以使用TextPair类来存储学生姓名和成绩,代码如下: ``` public class SortMapper extends Mapper<LongWritable, Text, TextPair, Text> { private TextPair pair = new TextPair(); public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split("\t"); pair.set(fields[0], fields[1]); context.write(pair, value); } } ``` 在这段代码中,我们首先将输入的一行数据拆分成学生姓名和成绩两个字段,然后使用TextPair类将它们作为Key输出,原始数据作为Value输出。 3. 编写Partitioner代码 Partitioner用于对Mapper的输出进行分区,以确保相同Key的数据被分配到同一个Reducer中。在自定义二次排序中,我们需要按照学生姓名进行分区,因此我们可以使用HashPartitioner来进行分区,代码如下: ``` public class SortPartitioner extends Partitioner<TextPair, Text> { public int getPartition(TextPair key, Text value, int numPartitions) { return (key.getFirst().hashCode() & Integer.MAX_VALUE) % numPartitions; } } ``` 在这段代码中,我们使用HashPartitioner将学生姓名的HashCode和Partition数取模来确定数据被分配到哪个Reducer中。 4. 编写GroupComparator代码 GroupComparator用于将相同学生姓名的数据分配到同一个Reducer中,代码如下: ``` public class SortGroupComparator extends WritableComparator { protected SortGroupComparator() { super(TextPair.class, true); } public int compare(WritableComparable a, WritableComparable b) { TextPair pair1 = (TextPair) a; TextPair pair2 = (TextPair) b; return pair1.getFirst().compareTo(pair2.getFirst()); } } ``` 在这段代码中,我们重载了compare方法,用于比较两个Key的学生姓名是否相同。 5. 编写SortComparator代码 SortComparator用于对每个Reducer中的数据进行排序,按照成绩从大到小排序,代码如下: ``` public class SortComparator extends WritableComparator { protected SortComparator() { super(TextPair.class, true); } public int compare(WritableComparable a, WritableComparable b) { TextPair pair1 = (TextPair) a; TextPair pair2 = (TextPair) b; int cmp = pair1.getFirst().compareTo(pair2.getFirst()); if (cmp != 0) { return cmp; } return -pair1.getSecond().compareTo(pair2.getSecond()); } } ``` 在这段代码中,我们首先比较两个Key的学生姓名是否相同,如果相同则比较成绩,否则直接返回姓名比较结果。 6. 编写Reducer代码 Reducer用于对Mapper的输出进行聚合和处理。在自定义二次排序中,我们只需要将每个学生的成绩按照从高到低的顺序输出即可,代码如下: ``` public class SortReducer extends Reducer<TextPair, Text, Text, Text> { public void reduce(TextPair key, Iterable<Text> values, Context context) throws IOException, InterruptedException { for (Text value : values) { context.write(key.getFirst(), value); } } } ``` 在这段代码中,我们首先输出学生姓名,然后按照原始数据的顺序输出。 7. 编写Driver代码 最后,我们需要编写Driver代码来启动MapReduce作业。代码如下: ``` public class SortDriver extends Configured implements Tool { public int run(String[] args) throws Exception { Job job = Job.getInstance(getConf()); job.setJarByClass(SortDriver.class); job.setMapperClass(SortMapper.class); job.setPartitionerClass(SortPartitioner.class); job.setGroupingComparatorClass(SortGroupComparator.class); job.setSortComparatorClass(SortComparator.class); job.setReducerClass(SortReducer.class); job.setMapOutputKeyClass(TextPair.class); job.setMapOutputValueClass(Text.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); return job.waitForCompletion(true) ? 0 : 1; } public static void main(String[] args) throws Exception { int exitCode = ToolRunner.run(new SortDriver(), args); System.exit(exitCode); } } ``` 在这段代码中,我们首先创建一个Job实例,然后设置Mapper、Partitioner、GroupComparator、SortComparator和Reducer等类。最后,我们指定输入路径和输出路径,并启动作业。 以上就是在Linux上使用Hadoop实现自定义二次排序的流程。通过这个例子,您可以了解到如何在Linux系统上使用MapReduce编程模型和Hadoop分布式文件存储和处理系统来处理大规模数据集。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值