JobSubmitter。顾名思义,它是MapReduce中作业提交者,而实际上JobSubmitter除了构造方法外。对外提供的唯一一个非private成员变量或方法就是submitJobInternal()方法,它是提交Job的内部方法,实现了提交Job的全部业务逻辑。

本文,我们将深入研究MapReduce中用于提交Job的组件JobSubmitter。

首先,我们先看下JobSubmitter的类成员变量。例如以下:

  1. // 文件系统FileSystem实例
  2. private FileSystem jtFs;
  3. // client通信协议ClientProtocol实例
  4. private ClientProtocol submitClient;
  5. // 提交作业的主机名
  6. private String submitHostName;
  7. // 提交作业的主机地址
  8. private String submitHostAddress;

它一共同拥有四个类成员变量,分别为:

1、文件系统FileSystem实例jtFs:用于操作作业执行须要的各种文件等。

2、client通信协议ClientProtocol实例submitClient:用于与集群交互。完毕作业提交、作业状态查询等;

3、提交作业的主机名submitHostName。

4、提交作业的主机地址submitHostAddress。

当中,client通信协议ClientProtocol实例submitClient是通过Cluster的client通信协议ClientProtocol实例client来赋值的,我们在《MapReduce源代码分析之新API作业提交(二):连接集群》一文中以前提到过。它依据MapReduce中參数mapreduce.framework.name的配置为yarn或local。有Yarn模式的YARNRunner和Local模式的LocalJobRunner两种情况。

接下来,我们再看下JobSubmitter的构造函数,例如以下:

  1. JobSubmitter(FileSystem submitFs, ClientProtocol submitClient)
  2. throws IOException {
  3.  
  4. // 依据入參赋值成员变量submitClient、jtFs
  5. this.submitClient = submitClient;
  6. this.jtFs = submitFs;
  7. }

非常easy,依据入參赋值成员变量submitClient、jtFs而已。

关键的来了,我们看下JobSubmitter唯一的对外核心功能方法submitJobInternal(),它被用于提交作业至集群,代码例如以下:

  1. /**
  2. * Internal method for submitting jobs to the system.
  3. *
  4. * <p>The job submission process involves:
  5. * <ol>
  6. * <li>
  7. * Checking the input and output specifications of the job.
  8. * </li>
  9. * <li>
  10. * Computing the {@link InputSplit}s for the job.
  11. * </li>
  12. * <li>
  13. * Setup the requisite accounting information for the
  14. * {@link DistributedCache} of the job, if necessary.
  15. * </li>
  16. * <li>
  17. * Copying the job's jar and configuration to the map-reduce system
  18. * directory on the distributed file-system.
  19. * </li>
  20. * <li>
  21. * Submitting the job to the <code>JobTracker</code> and optionally
  22. * monitoring it's status.
  23. * </li>
  24. * </ol></p>
  25. * @param job the configuration to submit
  26. * @param cluster the handle to the Cluster
  27. * @throws ClassNotFoundException
  28. * @throws InterruptedException
  29. * @throws IOException
  30. */
  31. JobStatus submitJobInternal(Job job, Cluster cluster)
  32. throws ClassNotFoundException, InterruptedException, IOException {
  33.  
  34. //validate the jobs output specs
  35. // 调用checkSpecs()方法,校验作业输出路径是否配置,且是否已存在,
  36. // 正确的情况应该是已配置且未存在,输出路径配置參数为mapreduce.output.fileoutputformat.outputdir,
  37. // 之前WordCount作业的输出路径配置为hdfs://nameservice1/output/output
  38. checkSpecs(job);
  39.  
  40. // 从作业job中获取配置信息conf
  41. Configuration conf = job.getConfiguration();
  42.  
  43. // 调用addMRFrameworkToDistributedCache()方法加入应用框架路径到分布式缓存中
  44. addMRFrameworkToDistributedCache(conf);
  45.  
  46. // 通过JobSubmissionFiles的getStagingDir()静态方法获取作业运行时阶段区域路径jobStagingArea
  47. // 取參数yarn.app.mapreduce.am.staging-dir。參数未配置默觉得/tmp/hadoop-yarn/staging
  48. // 然后后面是/提交作业username/.staging
  49. // 通过之前的WordCount任务的运行,我们查看历史记录,得知參数yarn.app.mapreduce.am.staging-dir配置的为/user。
  50. // 而提交作业username为hdfs,所以完整的路径应该为/user/hdfs/.staging
  51. Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);
  52.  
  53. //configure the command line options correctly on the submitting dfs
  54. // 获取当前本机地址
  55. InetAddress ip = InetAddress.getLocalHost();
  56.  
  57. // 确定提交作业的主机地址、主机名,并设置入配置信息conf,相应參数分别为
  58. // mapreduce.job.submithostname
  59. // mapreduce.job.submithostaddress
  60. if (ip != null) {
  61. submitHostAddress = ip.getHostAddress();
  62. submitHostName = ip.getHostName();
  63. conf.set(MRJobConfig.JOB_SUBMITHOST,submitHostName);
  64. conf.set(MRJobConfig.JOB_SUBMITHOSTADDR,submitHostAddress);
  65. }
  66.  
  67. // 生成作业ID。即JobID实例jobId
  68. JobID jobId = submitClient.getNewJobID();
  69.  
  70. // 将jobId设置入job
  71. job.setJobID(jobId);
  72.  
  73. // 构造提交作业路径Path实例submitJobDir,jobStagingArea后接/jobId。比方/job_1459913635503_0005
  74. // 之前WordCount作业的完整路径为/user/hdfs/.staging/job_1459913635503_0005
  75. Path submitJobDir = new Path(jobStagingArea, jobId.toString());
  76. JobStatus status = null;
  77.  
  78. // 设置作业一些參数:
  79. try {
  80.  
  81. // 设置mapreduce.job.user.name为当前用户。之前的WordCount演示样例配置的为hdfs用户
  82. conf.set(MRJobConfig.USER_NAME,
  83. UserGroupInformation.getCurrentUser().getShortUserName());
  84.  
  85. // 设置hadoop.http.filter.initializers为AmFilterInitializer
  86. conf.set("hadoop.http.filter.initializers",
  87. "org.apache.hadoop.yarn.server.webproxy.amfilter.AmFilterInitializer");
  88.  
  89. // 设置mapreduce.job.dir为submitJobDir,比方/user/hdfs/.staging/job_1459913635503_0005
  90. conf.set(MRJobConfig.MAPREDUCE_JOB_DIR, submitJobDir.toString());
  91. LOG.debug("Configuring job " + jobId + " with " + submitJobDir
  92. + " as the submit dir");
  93.  
  94. // get delegation token for the dir
  95. // 获取路径的授权令牌:调用TokenCache的obtainTokensForNamenodes()静态方法
  96. TokenCache.obtainTokensForNamenodes(job.getCredentials(),
  97. new Path[] { submitJobDir }, conf);
  98.  
  99. // 获取密钥和令牌。并将它们存储到令牌缓存TokenCache中
  100. populateTokenCache(conf, job.getCredentials());
  101.  
  102. // generate a secret to authenticate shuffle transfers
  103. if (TokenCache.getShuffleSecretKey(job.getCredentials()) == null) {
  104. KeyGenerator keyGen;
  105. try {
  106.  
  107. int keyLen = CryptoUtils.isShuffleEncrypted(conf)
  108. ? conf.getInt(MRJobConfig.MR_ENCRYPTED_INTERMEDIATE_DATA_KEY_SIZE_BITS,
  109. MRJobConfig.DEFAULT_MR_ENCRYPTED_INTERMEDIATE_DATA_KEY_SIZE_BITS)
  110. : SHUFFLE_KEY_LENGTH;
  111. keyGen = KeyGenerator.getInstance(SHUFFLE_KEYGEN_ALGORITHM);
  112. keyGen.init(keyLen);
  113. } catch (NoSuchAlgorithmException e) {
  114. throw new IOException("Error generating shuffle secret key", e);
  115. }
  116. SecretKey shuffleKey = keyGen.generateKey();
  117. TokenCache.setShuffleSecretKey(shuffleKey.getEncoded(),
  118. job.getCredentials());
  119. }
  120.  
  121. // 复制而且配置相关文件
  122. copyAndConfigureFiles(job, submitJobDir);
  123.  
  124. // 获取配置文件路径:job.xml
  125. Path submitJobFile = JobSubmissionFiles.getJobConfPath(submitJobDir);
  126.  
  127. // Create the splits for the job
  128. LOG.debug("Creating splits at " + jtFs.makeQualified(submitJobDir));
  129.  
  130. // 调用writeSplits()方法,写分片数据文件job.split和分片元数据文件job.splitmetainfo,
  131. // 并获得计算得到的map任务数目maps
  132. int maps = writeSplits(job, submitJobDir);
  133.  
  134. // 配置信息中设置map任务数目mapreduce.job.maps为上面得到的maps
  135. conf.setInt(MRJobConfig.NUM_MAPS, maps);
  136.  
  137. LOG.info("number of splits:" + maps);
  138.  
  139. // write "queue admins of the queue to which job is being submitted"
  140. // to job file.
  141.  
  142. // 获取作业队列名queue,取參数mapreduce.job.queuename,參数未配置默觉得default,
  143. // 之前的WordCount任务演示样例中,作业队列名queue就为default
  144. String queue = conf.get(MRJobConfig.QUEUE_NAME,
  145. JobConf.DEFAULT_QUEUE_NAME);
  146.  
  147. // 获取队列的訪问权限控制列表AccessControlList实例acl,通过client通信协议ClientProtocol实例submitClient的getQueueAdmins()方法。传入队列名queue。
  148. // 实际上之前的WordCount任务演示样例中,这里获取的是*
  149. AccessControlList acl = submitClient.getQueueAdmins(queue);
  150.  
  151. // 配置信息中设置队列參数mapred.queue.default.acl-administer-jobs
  152. // 之前的WordCount任务演示样例中。该參数被设置成为*
  153. conf.set(toFullPropertyName(queue,
  154. QueueACL.ADMINISTER_JOBS.getAclName()), acl.getAclString());
  155.  
  156. // removing jobtoken referrals before copying the jobconf to HDFS
  157. // as the tasks don't need this setting, actually they may break
  158. // because of it if present as the referral will point to a
  159. // different job.
  160. // 清空缓存的令牌
  161. TokenCache.cleanUpTokenReferral(conf);
  162.  
  163. // 依据參数确定是否须要追踪令牌ID
  164. // 取參数mapreduce.job.token.tracking.ids.enabled,參数未配置默觉得false
  165. if (conf.getBoolean(
  166. MRJobConfig.JOB_TOKEN_TRACKING_IDS_ENABLED,
  167. MRJobConfig.DEFAULT_JOB_TOKEN_TRACKING_IDS_ENABLED)) {
  168.  
  169. // 通过job获取令牌ID。并存储到trackingIds列表中
  170. // Add HDFS tracking ids
  171. ArrayList<String> trackingIds = new ArrayList<String>();
  172. for (Token<? extends TokenIdentifier> t :
  173. job.getCredentials().getAllTokens()) {
  174. trackingIds.add(t.decodeIdentifier().getTrackingId());
  175. }
  176.  
  177. // 将trackingIds列表中的内容设置到參数mapreduce.job.token.tracking.ids中
  178. conf.setStrings(MRJobConfig.JOB_TOKEN_TRACKING_IDS,
  179. trackingIds.toArray(new String[trackingIds.size()]));
  180. }
  181.  
  182. // Set reservation info if it exists
  183. // 如有必要,设置存在的预订信息
  184. // 參数为mapreduce.job.reservation.id
  185. ReservationId reservationId = job.getReservationId();
  186. if (reservationId != null) {
  187. conf.set(MRJobConfig.RESERVATION_ID, reservationId.toString());
  188. }
  189.  
  190. // Write job file to submit dir
  191. // 调用writeConf()方法,写入作业配置信息至文件job.xml
  192. writeConf(conf, submitJobFile);
  193.  
  194. //
  195. // Now, actually submit the job (using the submit name)
  196. // 调用printTokens()方法打印令牌信息到Log文件
  197. printTokens(jobId, job.getCredentials());
  198.  
  199. // 通过client通信协议ClientProtocol实例submitClient的submitJob()方法提交作业,
  200. // 并获取作业状态JobStatus实例status
  201. // 由集群连接一文的分析我们能够知道,这个submitClient实际上是YARNRunner或LocalJobRunner对象,
  202. // 终于调用的是二者的submitJob()方法。我们留待以后分析
  203. status = submitClient.submitJob(
  204. jobId, submitJobDir.toString(), job.getCredentials());
  205.  
  206. // 假设作业状态JobStatus实例status不为null。直接返回,否则抛出无法载入作业的IO异常
  207. if (status != null) {
  208. return status;
  209. } else {
  210. throw new IOException("Could not launch job");
  211. }
  212. } finally {
  213.  
  214. // 终于。抛出无法载入作业的IO异常前,调用文件系统FileSystem实例jtFs的delete()方法。
  215. // 删除作业提交的相关文件夹或文件submitJobDir
  216. if (status == null) {
  217. LOG.info("Cleaning up the staging area " + submitJobDir);
  218. if (jtFs != null && submitJobDir != null)
  219. jtFs.delete(submitJobDir, true);
  220.  
  221. }
  222. }
  223. }

submitJobInternal()方法篇幅比較长,逻辑也非常复杂,本文先介绍下它的大体逻辑。兴许分文会介绍各个环节的具体内容,且以下涉及到的之前WordCount作业演示样例在《Hadoop2.6.0版本号MapReudce演示样例之WordCount(一)》及其姊妹篇中。敬请注意!submitJobInternal()方法大体逻辑例如以下:

1、调用checkSpecs()方法,校验作业输出路径是否配置。且是否已存在:

正确的情况应该是已配置且未存在。输出路径配置參数为mapreduce.output.fileoutputformat.outputdir,之前WordCount作业的输出路径配置为hdfs://nameservice1/output/output。

2、从作业job中获取配置信息conf;

3、调用addMRFrameworkToDistributedCache()方法加入应用框架路径到分布式缓存中;

4、通过JobSubmissionFiles的getStagingDir()静态方法获取作业运行时阶段区域路径jobStagingArea:

取參数yarn.app.mapreduce.am.staging-dir,參数未配置默觉得/tmp/hadoop-yarn/staging,然后后面是/提交作业username/.staging,通过之前的WordCount任务的运行,我们查看历史记录,得知參数yarn.app.mapreduce.am.staging-dir配置的为/user,而提交作业username为hdfs,所以完整的路径应该为/user/hdfs/.staging;

5、获取当前本机地址ip。

6、确定提交作业的主机地址、主机名。并设置入配置信息conf,相应參数分别为mapreduce.job.submithostname、mapreduce.job.submithostaddress;

7、生成作业ID。即JobID实例jobId:

通过client通信协议ClientProtocol实例submitClient的getNewJobID()方法生成作业ID。即JobID实例jobId;

8、 将jobId设置入job;

9、构造提交作业路径Path实例submitJobDir:

jobStagingArea后接/jobId。比方/job_1459913635503_0005,之前WordCount作业的完整路径为/user/hdfs/.staging/job_1459913635503_0005;

10、设置作业一些參数:

10.1、设置mapreduce.job.user.name为当前用户。之前的WordCount演示样例配置的为hdfs用户。

10.2、设置hadoop.http.filter.initializers为AmFilterInitializer;

10.3、设置mapreduce.job.dir为submitJobDir,比方/user/hdfs/.staging/job_1459913635503_0005。

11、获取路径的授权令牌:调用TokenCache的obtainTokensForNamenodes()静态方法;

12、通过populateTokenCache()方法获取密钥和令牌。并将它们存储到令牌缓存TokenCache中;

14、复制而且配置相关文件:通过copyAndConfigureFiles()方法实现;

15、获取配置文件路径:job.xml;

16、调用writeSplits()方法。写分片数据文件job.split和分片元数据文件job.splitmetainfo,并获得计算得到的map任务数目maps;

17、配置信息中设置map任务数目mapreduce.job.maps为上面得到的maps。

18、获取作业队列名queue。取參数mapreduce.job.queuename。參数未配置默觉得default。之前的WordCount任务演示样例中。作业队列名queue就为default;

19、获取队列的訪问权限控制列表AccessControlList实例acl:

通过client通信协议ClientProtocol实例submitClient的getQueueAdmins()方法,传入队列名queue。实际上之前的WordCount任务演示样例中,这里获取的是*。

20、配置信息中设置队列參数mapred.queue.default.acl-administer-jobs,之前的WordCount任务演示样例中,该參数被设置成为*。

21、清空缓存的令牌:通过TokenCache的cleanUpTokenReferral()方法实现;

22、依据參数确定是否须要追踪令牌ID,假设须要的话:

取參数mapreduce.job.token.tracking.ids.enabled,參数未配置默觉得false。通过job获取令牌ID,并存储到trackingIds列表中。将trackingIds列表中的内容设置到參数mapreduce.job.token.tracking.ids中;

23、如有必要。设置存在的预订信息:參数为mapreduce.job.reservation.id;

24、调用writeConf()方法,写入作业配置信息至文件job.xml;

25、调用printTokens()方法打印令牌信息到Log文件;

26、通过client通信协议ClientProtocol实例submitClient的submitJob()方法提交作业。并获取作业状态JobStatus实例status:

由集群连接一文的分析我们能够知道,这个submitClient实际上是YARNRunner或LocalJobRunner对象。终于调用的是二者的submitJob()方法。我们留待以后分析。

27、假设作业状态JobStatus实例status不为null,直接返回,否则抛出无法载入作业的IO异常:

终于,抛出无法载入作业的IO异常前,调用文件系统FileSystem实例jtFs的delete()方法,删除作业提交的相关文件夹或文件submitJobDir。

总体流程如上,对于关键步骤的主要细节。限于篇幅。敬请关注《MapReduce源代码分析之JobSubmitter(二)》!

MapReduce源代码分析之JobSubmitter(一)的更多相关文章

  1. MapReduce源代码分析MapTask分析

    前言 MapReduce该分析是基于源代码Hadoop1.2.1代码分析进行的基础上. 该章节会分析在MapTask端的详细处理流程以及MapOutputCollector是怎样处理map之后的col ...

  2. MapReduce源代码分析之LocatedFileStatusFetcher

    LocatedFileStatusFetcher是MapReduce中一个针对给定输入路径数组,使用配置的线程数目来获取数据块位置的有用类. 它的主要作用就是利用多线程技术.每一个线程相应一个任务.每 ...

  3. MapReduce源码分析之JobSubmitter(一)

    JobSubmitter,顾名思义,它是MapReduce中作业提交者,而实际上JobSubmitter除了构造方法外,对外提供的唯一一个非private成员变量或方法就是submitJobInter ...

  4. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  5. 深入理解Spark 2.1 Core (十一):Shuffle Reduce 端的原理与源代码分析

    http://blog.csdn.net/u011239443/article/details/56843264 在<深入理解Spark 2.1 Core (九):迭代计算和Shuffle的原理 ...

  6. Spark源代码分析之六:Task调度(二)

    话说在<Spark源代码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这种方法针对接收到的ReviveOffe ...

  7. Hadoop源代码分析(完整版)

    Hadoop源代码分析(一) 关键字: 分布式云计算 Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施. GoogleCluster:http:// ...

  8. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  9. Twitter Storm源代码分析之ZooKeeper中的目录结构

    徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...

随机推荐

  1. 使用CreateRemoteThread把代码远程注入指定exe执行

    由于本人也是新手,如果有朋友不懂windows api相关知识,我相信查阅书籍或者百度会比我说有帮助的多,下面就我所做简单复述一下过程,欢迎指正缺点. 效果图示如下: 做的这个例子首先是创建了一个MF ...

  2. python读取大文件【一行一行读取】

    with open('e:/content.txt') as f: for line in f: if '==3346628==' in line: …………

  3. ZCMU训练赛-B(dp/暴力)

    B - Break Standard Weight The balance was the first mass measuring instrument invented. In its tradi ...

  4. HDU 2523 sort (hash)

    #include<iostream> #include<cstring> #include<cmath> #include<cstdio> using ...

  5. 【AC自动机】hdu2222 Keywords Search

    AC自动机模板题,给你n个模式串和一个文本串,问你有几个模式串在文本串出现过. 注意防止重复统计 这里推荐一波郭大爷的介绍,简单易懂. http://www.bilibili.com/video/av ...

  6. UTF8、UTF16、UTF16-LE、UTF16-BE、UTF32都是些什么?

    下述内容大部分引用自CSDN: Unicode 是 unicode.org 制定的编码标准,目前得到了绝大部分操作系统和编程语言的支持.unicode.org 官方对 Unicode 的定义是:Uni ...

  7. jdk_配置

    如果不知道jdk安装目录,可以使用如下命令查找到jdk安装目录 linux /usr/libexec/java_home windows版 新建系统变量JAVA_HOME(自己jdk的安装目录)C:\ ...

  8. 在sublime执行自定义脚本

    [背景] 一般项目都会有一个预处理的脚本, 在发布,或者预览效果的时候,往往要先执行脚本. 想法来自editplus 习惯了editplus的同学,都知道,可以配置自定义执行的脚本. 一般我会把它配置 ...

  9. vb6 wininet

    Private Const UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; .NET CLR 1.1.4322)&qu ...

  10. SpringBoot整合Quartz定时任务

    记录一个SpringBoot 整合 Quartz 的Demo实例 POM.XML文件 <!-- 定时器任务 quartz需要导入的坐标 --> <dependency> < ...