为了能使源码的执行过程与Hadoop权威指南(2、3版)中章节Shuffle and Sort的分析相对应,Hadoop的版本为0.20.2。

一般情况下我们通过Job(org.apache.hadoop.mapreduce.Job)的方法waitForCompletion来开始一个Job的执行。

  1. /**
  2. * Submit the job to the cluster and wait for it to finish.
  3. *
  4. * @param verbose
  5. * print the progress to the user
  6. * @return true if the job succeeded
  7. * @throws IOException
  8. * thrown if the communication with the <code>JobTracker</code>
  9. * is lost
  10. */
  11. public boolean waitForCompletion(boolean verbose) throws IOException,
  12. InterruptedException, ClassNotFoundException {
  13. if (state == JobState.DEFINE) {
  14. submit();
  15. }
  16.  
  17. if (verbose) {
  18. jobClient.monitorAndPrintJob(conf, info);
  19. } else {
  20. info.waitForCompletion();
  21. }
  22.  
  23. return isSuccessful();
  24. }

通常设置方法参数verbose为true,这样可以在控制台中看到Job的执行过程信息。

其中Job的具体提交过程是由方法submit完成的,

  1. /**
  2. * Submit the job to the cluster and return immediately.
  3. *
  4. * @throws IOException
  5. */
  6. public void submit() throws IOException, InterruptedException,
  7. ClassNotFoundException {
  8. ensureState(JobState.DEFINE);
  9.  
  10. setUseNewAPI();
  11.  
  12. info = jobClient.submitJobInternal(conf);
  13.  
  14. state = JobState.RUNNING;
  15. }

而submit方法的执行又依赖于JobClient submitJobInternal来完成,方法submitJobInternal是Job任务提交过程中的重点,在方法中完成的Job任务的初始化准备工作。

  1. /**
  2. * Internal method for submitting jobs to the system.
  3. *
  4. * @param job
  5. * the configuration to submit
  6. * @return a proxy object for the running job
  7. * @throws FileNotFoundException
  8. * @throws ClassNotFoundException
  9. * @throws InterruptedException
  10. * @throws IOException
  11. */
  12. public RunningJob submitJobInternal(JobConf job)
  13. throws FileNotFoundException, ClassNotFoundException,
  14. InterruptedException, IOException {
  15. /*
  16. * configure the command line options correctly on the submitting dfs
  17. */
  18. JobID jobId = jobSubmitClient.getNewJobId();
  19.  
  20. /*
  21. * 在submitJobDir目录下有三个文件:job.jar、job.split、job.xml
  22. *
  23. * **********************************************************************
  24. */
  25. Path submitJobDir = new Path(getSystemDir(), jobId.toString());
  26. Path submitJarFile = new Path(submitJobDir, "job.jar");
  27. Path submitSplitFile = new Path(submitJobDir, "job.split");
  28.  
  29. configureCommandLineOptions(job, submitJobDir, submitJarFile);
  30.  
  31. Path submitJobFile = new Path(submitJobDir, "job.xml");
  32.  
  33. /*
  34. * 获取reducer的数目
  35. *
  36. * **********************************************************************
  37. */
  38. int reduces = job.getNumReduceTasks();
  39.  
  40. JobContext context = new JobContext(job, jobId);
  41.  
  42. /*
  43. * Check the output specification
  44. *
  45. * 根据是否使用New API验证OutputFormat
  46. *
  47. * 如输出格式设置(未设置默认为TextOutputFormat)、是否设置输出路径及输出路径是否已经存在
  48. *
  49. * **********************************************************************
  50. */
  51. if (reduces == 0 ? job.getUseNewMapper() : job.getUseNewReducer()) {
  52. org.apache.hadoop.mapreduce.OutputFormat<?, ?> output = ReflectionUtils
  53. .newInstance(context.getOutputFormatClass(), job);
  54.  
  55. output.checkOutputSpecs(context);
  56. } else {
  57. job.getOutputFormat().checkOutputSpecs(fs, job);
  58. }
  59.  
  60. /*
  61. * Create the splits for the job
  62. *
  63. * *******************************************************************
  64. */
  65. LOG.debug("Creating splits at " + fs.makeQualified(submitSplitFile));
  66.  
  67. /*
  68. * 根据输入切片的数目决定map任务的数目
  69. *
  70. * 一个输入切片对应一个map
  71. *
  72. * *******************************************************************
  73. */
  74. int maps;
  75.  
  76. if (job.getUseNewMapper()) {
  77. maps = writeNewSplits(context, submitSplitFile);
  78. } else {
  79. maps = writeOldSplits(job, submitSplitFile);
  80. }
  81.  
  82. job.set("mapred.job.split.file", submitSplitFile.toString());
  83.  
  84. job.setNumMapTasks(maps);
  85.  
  86. /*
  87. * Write job file to JobTracker's fs
  88. *
  89. * **********************************************************************
  90. */
  91. FSDataOutputStream out = FileSystem.create(fs, submitJobFile,
  92. new FsPermission(JOB_FILE_PERMISSION));
  93.  
  94. try {
  95. job.writeXml(out);
  96. } finally {
  97. out.close();
  98. }
  99.  
  100. /*
  101. * Now, actually submit the job (using the submit name)
  102. *
  103. * **********************************************************************
  104. */
  105. JobStatus status = jobSubmitClient.submitJob(jobId);
  106.  
  107. if (status != null) {
  108. return new NetworkedJob(status);
  109. } else {
  110. throw new IOException("Could not launch job");
  111. }
  112. }

下面对该方法内部的执行流程进行详细分析:

(1)生成Job ID

  1. JobID jobId = jobSubmitClient.getNewJobId();

(2)目录相关及文件

在submitJobDir目录下有三个文件:job.jar、job.split、job.xml,其中

job.jar:Job相关类(资源)的一个Jar包;

job.split:Job的输入文件(可能有多个或可以是其它格式(如HBase HTable))会根据一定的条件进行切片,每一个切片中的“数据”会对应的Job的一个Map任务,即每一个Map仅处理某一个切片中的“数据”;

job.xml:用以保存Job相关的配置数据。

  1. /*
  2. * 在submitJobDir目录下有三个文件:job.jar、job.split、job.xml
  3. *
  4. * **********************************************************************
  5. */
  6. Path submitJobDir = new Path(getSystemDir(), jobId.toString());
  7. Path submitJarFile = new Path(submitJobDir, "job.jar");
  8. Path submitSplitFile = new Path(submitJobDir, "job.split");
  9.  
  10. /*
  11. * 根据命令行参数-libjars, -files, -archives对Job进行相应的配置
  12. */
  13. configureCommandLineOptions(job, submitJobDir, submitJarFile);
  14.  
  15. Path submitJobFile = new Path(submitJobDir, "job.xml");

其中,configureCommandLineOptions主要是根据用户在命令行环境下提供的参数(-libjars、-files、-archives)进行DistributedCache的设置,并将相应的Jar拷贝至目录submitJobDir中。

注:DistributedCache的相关知识会在后续分析,在此先不进行讨论。

(3)Reducer数目

获取用户所设置的该Job中Reducer的数目。

  1. int reduces = job.getNumReduceTasks();

(4)JobContext

  1. JobContext context = new JobContext(job, jobId);

其实JobContext就是对JobConf与JobID的封装。

(5)Job输出相关校验

  1. /*
  2. * Check the output specification
  3. *
  4. * 根据是否使用New API验证OutputFormat
  5. *
  6. * 如输出格式设置(未设置默认为TextOutputFormat)、是否设置输出路径及输出路径是否已经存在
  7. *
  8. * **********************************************************************
  9. */
  10. if (reduces == 0 ? job.getUseNewMapper() : job.getUseNewReducer()) {
  11. org.apache.hadoop.mapreduce.OutputFormat<?, ?> output = ReflectionUtils
  12. .newInstance(context.getOutputFormatClass(), job);
  13.  
  14. output.checkOutputSpecs(context);
  15. } else {
  16. job.getOutputFormat().checkOutputSpecs(fs, job);
  17. }

校验时会根据是否使用新版本的API分为两种情况,默认情况下使用的都是新版本的API,所以此处不考虑旧版本API的情况,所以分析的代码变为

 

  1. org.apache.hadoop.mapreduce.OutputFormat<?, ?> output = ReflectionUtils
  2. .newInstance(context.getOutputFormatClass(), job);
  3.  
  4. output.checkOutputSpecs(context);

首先,获取输出的具体格式;

  1. context.getOutputFormatClass()

 

  1. /**
  2. * Get the {@link OutputFormat} class for the job.
  3. *
  4. * @return the {@link OutputFormat} class for the job.
  5. */
  6. @SuppressWarnings("unchecked")
  7. public Class<? extends OutputFormat<?, ?>> getOutputFormatClass()
  8. throws ClassNotFoundException {
  9. return (Class<? extends OutputFormat<?, ?>>) conf.getClass(
  10. OUTPUT_FORMAT_CLASS_ATTR, TextOutputFormat.class);
  11. }

由上述代码可以看出,如果用户并没有明确指定输出格式类型,则默认使用TextOutputFormat。

注:文本是进行数据分析时经常使用的一种格式,因此本文主要使用TextInputFormat、TextOutputFormat进行讲解。

然后,通过反射将输出格式实例化;

  1. org.apache.hadoop.mapreduce.OutputFormat<?, ?> output = ReflectionUtils
  2. .newInstance(context.getOutputFormatClass(), job);

最后,通过输出格式的具体类型进行校验,包括两个部分:是否设置输出目录及输出目录是否已经存在。

TextOutputFormat的checkOutputSpecs继承自它的父类FileOutputFormat。

  1. public void checkOutputSpecs(JobContext job)
  2. throws FileAlreadyExistsException, IOException {
  3. // Ensure that the output directory is set and not already there
  4. Path outDir = getOutputPath(job);
  5.  
  6. if (outDir == null) {
  7. throw new InvalidJobConfException("Output directory not set.");
  8. }
  9.  
  10. if (outDir.getFileSystem(job.getConfiguration()).exists(outDir)) {
  11. throw new FileAlreadyExistsException("Output directory " + outDir
  12. + " already exists");
  13. }
  14. }

(6)生成输入切片(Split),并设置Map的数目;

  1. /*
  2. * Create the splits for the job
  3. *
  4. * *******************************************************************
  5. */
  6. LOG.debug("Creating splits at " + fs.makeQualified(submitSplitFile));
  7.  
  8. /*
  9. * 根据输入切片的数目决定map任务的数目
  10. *
  11. * 一个输入切片对应一个map
  12. *
  13. * *******************************************************************
  14. */
  15. int maps;
  16.  
  17. if (job.getUseNewMapper()) {
  18. maps = writeNewSplits(context, submitSplitFile);
  19. } else {
  20. maps = writeOldSplits(job, submitSplitFile);
  21. }
  22.  
  23. job.set("mapred.job.split.file", submitSplitFile.toString());
  24.  
  25. job.setNumMapTasks(maps);

这里仅分析新版本API下的writeNewSplits,该方法需要两个参数:JobContext及切片文件的Path。

  1. @SuppressWarnings("unchecked")
  2. private <T extends org.apache.hadoop.mapreduce.InputSplit> int writeNewSplits(
  3. JobContext job, Path submitSplitFile) throws IOException,
  4. InterruptedException, ClassNotFoundException {
  5. JobConf conf = job.getJobConf();
  6.  
  7. /*
  8. * 创建InputFormat实例
  9. *
  10. * 不同的InputFormat实例获取Split的方式不同
  11. *
  12. * ******************************************************************
  13. */
  14. org.apache.hadoop.mapreduce.InputFormat<?, ?> input = ReflectionUtils
  15. .newInstance(job.getInputFormatClass(), job.getJobConf());
  16.  
  17. /*
  18. * 获取输入文件对应的切片记录
  19. *
  20. * ******************************************************************
  21. */
  22. List<org.apache.hadoop.mapreduce.InputSplit> splits = input
  23. .getSplits(job);
  24.  
  25. T[] array = (T[]) splits
  26. .toArray(new org.apache.hadoop.mapreduce.InputSplit[splits
  27. .size()]);
  28.  
  29. /*
  30. * sort the splits into order based on size, so that the biggest go
  31. * first
  32. *
  33. * ******************************************************************
  34. */
  35. Arrays.sort(array, new NewSplitComparator());
  36.  
  37. /*
  38. * 写出SplitFile
  39. *
  40. * ******************************************************************
  41. */
  42.  
  43. // 打开切片文件输出流,并写出头信息(头、版本号、切片数目)
  44. DataOutputStream out = writeSplitsFileHeader(conf, submitSplitFile,
  45. array.length);
  46.  
  47. try {
  48. if (array.length != 0) {
  49. DataOutputBuffer buffer = new DataOutputBuffer();
  50.  
  51. RawSplit rawSplit = new RawSplit();
  52.  
  53. SerializationFactory factory = new SerializationFactory(conf);
  54.  
  55. Serializer<T> serializer = factory
  56. .getSerializer((Class<T>) array[0].getClass());
  57.  
  58. serializer.open(buffer);
  59.  
  60. for (T split : array) {
  61. rawSplit.setClassName(split.getClass().getName());
  62.  
  63. buffer.reset();
  64.  
  65. // 序列化文件名、起始位置、切片长度、主机位置(多个)
  66. serializer.serialize(split);
  67.  
  68. rawSplit.setDataLength(split.getLength());
  69.  
  70. rawSplit.setBytes(buffer.getData(), 0, buffer.getLength());
  71.  
  72. rawSplit.setLocations(split.getLocations());
  73.  
  74. rawSplit.write(out);
  75. }
  76.  
  77. serializer.close();
  78. }
  79. } finally {
  80. out.close();
  81. }
  82.  
  83. return array.length;
  84. }

方法思路:根据指定的输入格式类型(InputFormat)对输入文件进行切片,并将切片信息保存至指定的切片文件中。

注:切片并不是对输入文件进行物理上的切割,而只是一种逻辑上的“分割”,即将输入文件某个片段的起始位置保存下来,后期Map任务运行时根据切片文件就可以将该片段作为输入进行处理。

首先,获取输入格式类型,

  1. job.getInputFormatClass()

 

  1. /**
  2. * Get the {@link InputFormat} class for the job.
  3. *
  4. * @return the {@link InputFormat} class for the job.
  5. */
  6. @SuppressWarnings("unchecked")
  7. public Class<? extends InputFormat<?, ?>> getInputFormatClass()
  8. throws ClassNotFoundException {
  9. return (Class<? extends InputFormat<?, ?>>) conf.getClass(
  10. INPUT_FORMAT_CLASS_ATTR, TextInputFormat.class);
  11. }

与输出格式类型相同,如果用户没有特殊指定,默认的输入格式类型为TextInputFormat,然后将此输入格式类型实例化。

  1. org.apache.hadoop.mapreduce.InputFormat<?, ?> input = ReflectionUtils
  2. .newInstance(job.getInputFormatClass(), job.getJobConf());

然后,根据具体的输入格式类型计算切片信息,

  1. /*
  2. * 获取输入文件对应的切片记录
  3. *
  4. * ******************************************************************
  5. */
  6. List<org.apache.hadoop.mapreduce.InputSplit> splits = input
  7. .getSplits(job);

TextInputFormat的方法getSplits继承自它的父类FileInputFormat。

  1. /**
  2. * Generate the list of files and make them into FileSplits.
  3. */
  4. public List<InputSplit> getSplits(JobContext job) throws IOException {
  5. /*
  6. * 计算Split的最小值与最大值
  7. *
  8. * ********************************************************************
  9. */
  10. long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
  11. long maxSize = getMaxSplitSize(job);
  12.  
  13. // generate splits
  14. List<InputSplit> splits = new ArrayList<InputSplit>();
  15.  
  16. /*
  17. * 逐个处理InputPaths中的文件
  18. *
  19. * *******************************************************************
  20. */
  21. for (FileStatus file : listStatus(job)) {
  22. Path path = file.getPath();
  23.  
  24. FileSystem fs = path.getFileSystem(job.getConfiguration());
  25.  
  26. /*
  27. * 获取特定文件的长度
  28. *
  29. * ******************************************************************
  30. */
  31. long length = file.getLen();
  32.  
  33. /*
  34. * 获取特定文件对应的块Block信息
  35. *
  36. * ***************************************************************
  37. */
  38. BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0,
  39. length);
  40.  
  41. /*
  42. * 如果文件长度大于0且是可切片的
  43. *
  44. * ***************************************************************
  45. */
  46. if ((length != 0) && isSplitable(job, path)) {
  47. long blockSize = file.getBlockSize();
  48.  
  49. /*
  50. * 根据blockSize、minSize、maxSize计算切片大小
  51. *
  52. * Math.max(minSize, Math.min(maxSize, blockSize)
  53. *
  54. * ***********************************************************
  55. */
  56. long splitSize = computeSplitSize(blockSize, minSize, maxSize);
  57.  
  58. long bytesRemaining = length;
  59.  
  60. while (((double) bytesRemaining) / splitSize > SPLIT_SLOP) {
  61. /*
  62. * 返回的Block Index为此切片开始位置所在Block的Index
  63. *
  64. * **********************************************************
  65. */
  66. int blkIndex = getBlockIndex(blkLocations, length
  67. - bytesRemaining);
  68.  
  69. /*
  70. * 一个Block对应一个FileSplit
  71. *
  72. * *******************************************************
  73. */
  74. splits.add(new FileSplit(path, length - bytesRemaining,
  75. splitSize, blkLocations[blkIndex].getHosts()));
  76.  
  77. bytesRemaining -= splitSize;
  78. }
  79.  
  80. if (bytesRemaining != 0) {
  81. /*
  82. * 剩余的文件数据形成一个切片,hosts为此文件最后一个Block的hosts
  83. *
  84. * **********************************************************
  85. */
  86. splits.add(new FileSplit(path, length - bytesRemaining,
  87. bytesRemaining,
  88. blkLocations[blkLocations.length - 1].getHosts()));
  89. }
  90. } else if (length != 0) {
  91. /*
  92. * 文件长度不为0但不可分割
  93. *
  94. * 不能切片的文件,整体形成一个切片,hosts为此文件第一个Block的hosts
  95. *
  96. * ***********************************************************
  97. */
  98. splits.add(new FileSplit(path, 0, length, blkLocations[0]
  99. .getHosts()));
  100. } else {
  101. // Create empty hosts array for zero length files
  102. splits.add(new FileSplit(path, 0, length, new String[0]));
  103. }
  104. }
  105.  
  106. LOG.debug("Total # of splits: " + splits.size());
  107.  
  108. return splits;
  109. }

getSplits处理流程如下:

① 根据配置参数计算Split所允许的最小值与最大值,为后期确定Split的长度提供参考;

  1. /*
  2. * 计算Split的最小值与最大值
  3. *
  4. * ********************************************************************
  5. */
  6. long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
  7. long maxSize = getMaxSplitSize(job);

② 在内存中创建相应的数据结构,用以保存计算所得的切片信息;

  1. // generate splits
  2. List<InputSplit> splits = new ArrayList<InputSplit>();

③ 循环处理InputPaths所添加的文件,对一个文件各自计算其对应的切片信息;

  1. /*
  2. * 逐个处理InputPaths中的文件
  3. *
  4. * *******************************************************************
  5. */
  6. for (FileStatus file : listStatus(job)) {
  7. ......
  8. }

④ 计算某个文件的切片信息:

a. 获取该文件的长度及对应的Block信息;

  1. Path path = file.getPath();
  2.  
  3. FileSystem fs = path.getFileSystem(job.getConfiguration());
  4.  
  5. /*
  6. * 获取特定文件的长度
  7. *
  8. * ******************************************************************
  9. */
  10. long length = file.getLen();
  11.  
  12. /*
  13. * 获取特定文件对应的块Block信息
  14. *
  15. * ***************************************************************
  16. */
  17. BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0,
  18. length);

b. 根据文件长度以及该文件是否可以切片,分为三种情况处理:

其中,文件是否支持Split,是由该文件类型对应的InputFormat决定的,FileInputFormat中的实现如下:

  1. /**
  2. * Is the given filename splitable? Usually, true, but if the file is stream
  3. * compressed, it will not be.
  4. *
  5. * <code>FileInputFormat</code> implementations can override this and return
  6. * <code>false</code> to ensure that individual input files are never
  7. * split-up so that {@link Mapper}s process entire files.
  8. *
  9. * @param context
  10. * the job context
  11. * @param filename
  12. * the file name to check
  13. * @return is this file splitable?
  14. */
  15. protected boolean isSplitable(JobContext context, Path filename) {
  16. return true;
  17. }

TextInputFormat中重写了该方法:

  1. @Override
  2. protected boolean isSplitable(JobContext context, Path file) {
  3. CompressionCodec codec = new CompressionCodecFactory(
  4. context.getConfiguration()).getCodec(file);
  5.  
  6. return codec == null;
  7. }

由上述代码可见,如果文本文件经过相应的压缩之后,是不支持进行Split的。

第一种情况:文件长度大于0,且文件支持Split;

首先计算一个切片的具体长度,长度的计算方式为:Math.max(minSize, Math.min(maxSize, blockSize) ;

  1. long blockSize = file.getBlockSize();
  2.  
  3. /*
  4. * 根据blockSize、minSize、maxSize计算切片大小
  5. *
  6. * Math.max(minSize, Math.min(maxSize, blockSize)
  7. *
  8. * ***********************************************************
  9. */
  10. long splitSize = computeSplitSize(blockSize, minSize, maxSize);

然后,根据splitSize进行切片,思路就是从文件开始处,以splitSize为区间,对文件进行逻辑上的切分;

  1. long bytesRemaining = length;
  2.  
  3. while (((double) bytesRemaining) / splitSize > SPLIT_SLOP) {
  4. /*
  5. * 返回的Block Index为此切片开始位置所在Block的Index
  6. *
  7. * **********************************************************
  8. */
  9. int blkIndex = getBlockIndex(blkLocations, length
  10. - bytesRemaining);
  11.  
  12. /*
  13. * 一个Block对应一个FileSplit
  14. *
  15. * *******************************************************
  16. */
  17. splits.add(new FileSplit(path, length - bytesRemaining,
  18. splitSize, blkLocations[blkIndex].getHosts()));
  19.  
  20. bytesRemaining -= splitSize;
  21. }
  22.  
  23. if (bytesRemaining != 0) {
  24. /*
  25. * 剩余的文件数据形成一个切片,hosts为此文件最后一个Block的hosts
  26. *
  27. * **********************************************************
  28. */
  29. splits.add(new FileSplit(path, length - bytesRemaining,
  30. bytesRemaining,
  31. blkLocations[blkLocations.length - 1].getHosts()));
  32. }

为了不产生过小的切片,要求尚未进行切片的文件部分长度(bytesRemaining)大于切片长度(splitSize)的SPLIT_SLOP(1.1)倍,然后将文件的剩余部分直接作为一个切片。

在上述代码中的切片信息中,还保存着切片对应的Block信息,注意切片并不一定会与Block完全吻合(即切片在文件中的起止处与该Block在文件中的起止处一致),所谓的对应的Block,是指该切片的起始处正在落在该Block的区间内;之所以要保存切片对应的Block信息,是为后期Map任务的“本地计算”调度运行作准备的。

第二种情况:文件长度大于0,但该文件不支持切片;

  1. /*
  2. * 文件长度不为0但不可分割
  3. *
  4. * 不能切片的文件,整体形成一个切片,hosts为此文件第一个Block的hosts
  5. *
  6. * ***********************************************************
  7. */
  8. splits.add(new FileSplit(path, 0, length, blkLocations[0]
  9. .getHosts()));

因为该文件不支持切片,直接将整个文件作为一个切片就可以了。

第三种情况:文件长度为0;

  1. // Create empty hosts array for zero length files
  2. splits.add(new FileSplit(path, 0, length, new String[0]));

此时直接创建一个空的切片即可。

到此,所有输入文件的切片信息就全部产生完毕了。

⑤ 对产生的切片进行排序处理,排序的依据是切片的大小,切片越大,在切片集合中的位置应该更靠前,这样可以使大的切片在调度时,优先得到处理。

  1. T[] array = (T[]) splits
  2. .toArray(new org.apache.hadoop.mapreduce.InputSplit[splits
  3. .size()]);
  4.  
  5. /*
  6. * sort the splits into order based on size, so that the biggest go
  7. * first
  8. *
  9. * ******************************************************************
  10. */
  11. Arrays.sort(array, new NewSplitComparator());

⑥ 存储切片信息至相应的切片文件中,调度任务时使用切片文件中的信息进行调度,具体的存储过程不影响整个处理流程的理解,在此不对它进行分析。

至此,writeNewSplits方法结果,该方法还回返回切片的总数目,即对应着Job的Map任务数目。

(7)将Job的相关信息写入job.xml;

  1. /*
  2. * Write job file to JobTracker's fs
  3. *
  4. * **********************************************************************
  5. */
  6. FSDataOutputStream out = FileSystem.create(fs, submitJobFile,
  7. new FsPermission(JOB_FILE_PERMISSION));
  8.  
  9. try {
  10. job.writeXml(out);
  11. } finally {
  12. out.close();
  13. }

(8)完成Job任务的实际提交;

  1. /*
  2. * Now, actually submit the job (using the submit name)
  3. *
  4. * **********************************************************************
  5. */
  6. JobStatus status = jobSubmitClient.submitJob(jobId);
  7.  
  8. if (status != null) {
  9. return new NetworkedJob(status);
  10. } else {
  11. throw new IOException("Could not launch job");
  12. }

到此,Job任务的提交过程分析完毕。

MapReduce执行过程源码分析(一)——Job任务的提交的更多相关文章

  1. JSP运行原理以及执行过程源码分析

    我们在开发JavaWeb的过程中,可能有过这样的疑问,当我们编写jsp页面时,不用引用不用创建就可以使用request.session.application对象,当使用浏览器访问JSP页面时,查看页 ...

  2. Hadoop2 使用 YARN 运行 MapReduce 的过程源码分析

    Hadoop 使用 YARN 运行 MapReduce 的过程如下图所示: 总共分为11步. 这里以 WordCount 为例, 我们在客户端终端提交作业: # 把本地的 /home/hadoop/t ...

  3. MapReduce过程源码分析

    MapReduce过程源码分析 Mapper   首先mapper完成映射,将word映射成(word,1)的形式.   MapReduce进程,Map阶段也叫MapTask,在MapTask中会通过 ...

  4. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  5. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  6. Spring加载流程源码分析03【refresh】

      前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...

  7. 【高速接口-RapidIO】5、Xilinx RapidIO核例子工程源码分析

    提示:本文的所有图片如果不清晰,请在浏览器的新建标签中打开或保存到本地打开 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:Vivado2015.4.2 ...

  8. 5.Xilinx RapidIO核例子工程源码分析

    https://www.cnblogs.com/liujinggang/p/10091216.html 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:V ...

  9. Flume-NG启动过程源码分析(二)(原创)

    在上一节中讲解了——Flume-NG启动过程源码分析(一)(原创)  本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatch ...

随机推荐

  1. PHP安全编程:跨站请求伪造CSRF的防御(转)

    跨站请求伪造(CSRF)是一种允许攻击者通过受害者发送任意HTTP请求的一类攻击方法.此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是攻击者.这样,很你就很难确定哪些请求是属于跨站 ...

  2. git使用介绍

    Git简单介绍 参考网址: git使用简介 这个教程推荐使用:git教程 git和svn的差异 git和svn的最大差异在于git是分布式的管理方式而svn是集中式的管理方式.如果不习惯用代码管理工具 ...

  3. [转] Python list、tuple、dict区别

    from: http://www.cnblogs.com/Michael-Kong/archive/2012/07/11/2585840.html Dictionary 是 Python 的内置数据类 ...

  4. redis 记录

    参考 :  http://keenwon.com/1275.html http://blog.csdn.net/freebird_lb/article/details/7733970 http://w ...

  5. Android(java)学习笔记243:多媒体之视频播放器

    1.这里我们还是利用案例演示视频播放器的使用: (1)首先,我们看看布局文件activity_main.xml,如下: <RelativeLayout xmlns:android="h ...

  6. Python开发【第七篇】:面向对象 和 python面向对象进阶篇(下)

    Python开发[第七篇]:面向对象   详见:<Python之路[第五篇]:面向对象及相关> python 面向对象(进阶篇)   上一篇<Python 面向对象(初级篇)> ...

  7. codevs 2541 幂运算(迭代加深搜索)

    /* 一开始想到了简单的深搜 维护当前可用的mi数组 然后回溯用哪个 不断更新新产生的mi 这样的问题是 由于mi不断产生 搜索规模扩大 不好 不好 下面是奇丑的WA掉的代码 做个反面教材 */ #i ...

  8. 在imge控件中直接显示图片(图片是byte[]格式)

    在工作过程中遇到了这个问题,在网上查了一些资料,结合自己的解决方法及解决过程总结了下,方面以后查阅.如果能帮到同样遇到这个问题的你,将非常高兴哦~_~ 由于asp.net中的Image控件是在Syst ...

  9. bootstrap datetimepicker 时间段选择限制

    <!DOCTYPE html> <html> <head> <title></title> <link href="./bo ...

  10. 自签名SSL生成

    本教程以AppServ生成自签名证书为例,同时配置OpenSSL环境1.生成自签名证书,控制台程序进入Apache目录下的bin目录 >openssl req -config ../conf/o ...