上一小节(http://www.cnblogs.com/lxf20061900/p/3643581.html)讲到Job. submit()方法中的:

info = jobClient.submitJobInternal(conf)方法用来上传资源提交Job的,这一节就讲讲这个方法。

一、首先jobClient在构造函数中会构造了和JobTracker通信的对象jobSubmitClient,jobSubmitClient是JobSubmissionProtocol类型的动态代理类。JobSubmissionProtocol协议是JobClient与JobTracker通信专用协议。代码如下:

  1. private static JobSubmissionProtocol createRPCProxy(InetSocketAddress addr,
  2. Configuration conf) throws IOException {
  3. return (JobSubmissionProtocol) RPC.getProxy(JobSubmissionProtocol.class,
  4. JobSubmissionProtocol.versionID, addr,
  5. UserGroupInformation.getCurrentUser(), conf,
  6. NetUtils.getSocketFactory(conf, JobSubmissionProtocol.class));
  7. }

  getProxy方法的关键是Invoker类,Invoker类实现了 InvocationHandler接口,主要有两个成员变量,remoteId是Client.ConnectionId类型,保存连接地址和用户的 ticket,客户端连接服务器由<remoteAddress,protocol,ticket>唯一标识。

Invoker类的invoke方法最重要的操作是:ObjectWritable value = (ObjectWritable) client.call(new Invocation(method, args), remoteId)。Invocation实现了Writable接口,并封装了method和args,使得可以通过RPC传输;Client.call方法将Writable参数封装到一个Call中,并且连接JobTracker后将封装后call发送过去,同步等待call执行完毕,返回value。

  1. public Writable call(Writable param, ConnectionId remoteId)
  2. throws InterruptedException, IOException {
  3. Call call = new Call(param);
  4. Connection connection = getConnection(remoteId, call);
  5. connection.sendParam(call); // send the parameter
  6. boolean interrupted = false;
  7. synchronized (call) {
  8. while (!call.done) {
  9. try {
  10. call.wait(); // wait for the result
  11. } catch (InterruptedException ie) {
  12. // save the fact that we were interrupted
  13. interrupted = true;
  14. }
  15. }
  16.  
  17. if (interrupted) {
  18. // set the interrupt flag now that we are done waiting
  19. Thread.currentThread().interrupt();
  20. }
  21.  
  22. if (call.error != null) {
  23. if (call.error instanceof RemoteException) {
  24. call.error.fillInStackTrace();
  25. throw call.error;
  26. } else { // local exception
  27. // use the connection because it will reflect an ip change, unlike
  28. // the remoteId
  29. throw wrapException(connection.getRemoteAddress(), call.error);
  30. }
  31. } else {
  32. return call.value;
  33. }
  34. }
  35. }

  上面的第四行代码用于建立同JobTracker的连接。而Client.getConnection方法中connection.setupIOstreams()才是真正建立连接的地方,其中的socket是通过默认的SocketFactory .createSocket(),而这个默认的SocketFactory是org.apache.hadoop.net. StandardSocketFactory。

二、jobClient.submitJobInternal(conf)初始化staging目录(这是job提交的根目录):Path jobStagingArea=JobSubmissionFiles.getStagingDir(JobClient.this, jobCopy),这个方法最终会调用jobTracker.getStagingAreaDirInternal()方法,代码如下:

  1. private String getStagingAreaDirInternal(String user) throws IOException {
  2. final Path stagingRootDir =
  3. new Path(conf.get("mapreduce.jobtracker.staging.root.dir",
  4. "/tmp/hadoop/mapred/staging"));
  5. final FileSystem fs = stagingRootDir.getFileSystem(conf);
  6. return fs.makeQualified(new Path(stagingRootDir,
  7. user+"/.staging")).toString();
  8. }

三、从JobTracker获取JobID。JobID jobId = jobSubmitClient.getNewJobId()。最终调用的是JobTracker.getNewJobId()方法。然后执行Path submitJobDir = new Path(jobStagingArea, jobId.toString());获得该job提交的路径,也就是在stagingDir目录下建一个以jobId为文件名的目录,可以查看配置文件中的"mapreduce.job.dir"来查看此完整目录。有了 submitJobDir之后就可以将job运行所需的全部文件上传到对应的目录下了,具体是调用 jobClient.copyAndConfigureFiles(jobCopy, submitJobDir)这个方法。

四、copyAndConfigureFiles(jobCopy, submitJobDir)实现上传文件,包括-tmpfiles(外部文件)、tmpjars(第三方jar包)、tmparchives(一些归档文件)以及job.jar拷贝到HDFS中,这个方法最终调用jobClient.copyAndConfigureFiles(job, jobSubmitDir, replication);这个方法实现文件上传。而前三种文件(tmpfiles(外部文件)、tmpjars(第三方jar包)、tmparchives(一些归档文件))的实际上传过程在copyRemoteFiles方法中,通过FileUtil.copy完成拷贝,这三种文件都是先分割文件列表后分别上传(每一类文件可以有多个)。然后是:

  1.    // First we check whether the cached archives and files are legal.
  2. TrackerDistributedCacheManager.validate(job);
  3. // set the timestamps of the archives and files
  4. TrackerDistributedCacheManager.determineTimestamps(job);
  5. // set the public/private visibility of the archives and files
  6. TrackerDistributedCacheManager.determineCacheVisibilities(job);
  7. // get DelegationTokens for cache files
  8. TrackerDistributedCacheManager.getDelegationTokens(job,job.getCredentials());

上面的代码是进行一些cached archives and files的校验和保存其时间戳和权限内容

  Job.jar通过fs.copyFromLocalFile方法拷贝到HDFS中。而job.jar(这是打包后的作业)文件则是直接通过fs.copyFromLocalFile(new Path(originalJarPath), submitJarFile);上传完成。我们在提交作业的时候会在本地先打包成jar文件然后将配置文件中的"mapred.jar"设置为本地jar包路径,当在这里拷贝到HDFS中后在重新将"mapred.jar"设置为HDFS对应job.jar包的路径。

  同时这四个文件都会设置replication个副本,防止热点出现。

五、然后就会根据我们设置的outputFormat类执行output.checkOutputSpecs(context),进行输出路径的检验,主要是保证输出路径不存在,存在会抛出异常。

六、对输入文件进行分片操作了,int maps = writeSplits(context, submitJobDir)。writeSplits方法会根据是否使用了新API选择不同的方法写:

  1. private int writeSplits(org.apache.hadoop.mapreduce.JobContext job,
  2. Path jobSubmitDir) throws IOException,
  3. InterruptedException, ClassNotFoundException {
  4. JobConf jConf = (JobConf)job.getConfiguration();
  5. int maps;
  6. if (jConf.getUseNewMapper()) {
  7. maps = writeNewSplits(job, jobSubmitDir);
  8. } else {
  9. maps = writeOldSplits(jConf, jobSubmitDir);
  10. }
  11. return maps;
  12. }
  1. 使用了新API后,会调用writeNewSplits(job, jobSubmitDir)方法,这个方法代码如下:
  1. private <T extends InputSplit>
  2. int writeNewSplits(JobContext job, Path jobSubmitDir) throws IOException,
  3. InterruptedException, ClassNotFoundException {
  4. Configuration conf = job.getConfiguration();
  5. InputFormat<?, ?> input =
  6. ReflectionUtils.newInstance(job.getInputFormatClass(), conf);//默认是TextInputFormat
  7.  
  8. List<InputSplit> splits = input.getSplits(job);
  9. T[] array = (T[]) splits.toArray(new InputSplit[splits.size()]);
  10.  
  11. // sort the splits into order based on size, so that the biggest
  12. // go first,大文件优先处理
  13. Arrays.sort(array, new SplitComparator());
  14. JobSplitWriter.createSplitFiles(jobSubmitDir, conf,
  15. jobSubmitDir.getFileSystem(conf), array);
  16. return array.length;//这是mapper的数量
  17. }
  1.  

可以看出该方法首先获取splits数组信息后,排序,将会优先处理大文件。JobSplitWriter.createSplitFiles(jobSubmitDir, conf, jobSubmitDir.getFileSystem(conf), array)方法会将split信息和SplitMetaInfo都写入HDFS中,其代码如下:

  1. public static <T extends InputSplit> void createSplitFiles(Path jobSubmitDir,
  2. Configuration conf, FileSystem fs, T[] splits)
  3. throws IOException, InterruptedException {
  4. FSDataOutputStream out = createFile(fs,
  5. JobSubmissionFiles.getJobSplitFile(jobSubmitDir), conf);
  6. SplitMetaInfo[] info = writeNewSplits(conf, splits, out);
  7. out.close();
  8. writeJobSplitMetaInfo(fs,JobSubmissionFiles.getJobSplitMetaFile(jobSubmitDir),
  9. new FsPermission(JobSubmissionFiles.JOB_FILE_PERMISSION), splitVersion,
  10. info);
  11. }

如上writeNewSplits会将信息写入job.split文件,然后返回SplitMetaInfo数组信息,再通过writeJobSplitMetaInfo方法将SplitMetaInfo信息写入job.splitmetainfo中。

七、然后将配置文件写入:jobCopy.writeXml(out);//写"job.xml"。

八、通过 jobSubmitClient.submitJob(jobId, submitJobDir.toString(), jobCopy.getCredentials())提交job,最终调用的是JobTracker.submitJob。

九、返回一个NetworkedJob(status, prof, jobSubmitClient)对象,它实现了RunningJob接口。这个对象可以在JobClient端(比如eclipse,不断的打印运行日志)。

ps:

一、hadoop版本是1.0.0;

二、上述文件的提交目录可以在web ui中打开相应作业的配置文件查找"mapreduce.job.dir",就可以看到文件的上传目录。比如:hdfs://XXXX:8020/user/hadoop/.staging/job_201403141637_0160

下一节关注上述的步骤八。

错误之处还望大伙指点

参考:

http://www.kankanews.com/ICkengine/archives/87415.shtml

mapreduce job提交流程源码级分析(二)(原创)的更多相关文章

  1. mapreduce job提交流程源码级分析(三)

    mapreduce job提交流程源码级分析(二)(原创)这篇文章说到了jobSubmitClient.submitJob(jobId, submitJobDir.toString(), jobCop ...

  2. mapreduce job提交流程源码级分析(一)(原创)

    首先,在自己写的MR程序中通过org.apache.hadoop.mapreduce.Job来创建Job.配置好之后通过waitForCompletion方法来提交Job并打印MR执行过程的log.H ...

  3. MapReduce之Job提交流程源码和切片源码分析

    hadoop2.7.2 MapReduce Job提交源码及切片源码分析 首先从waitForCompletion函数进入 boolean result = job.waitForCompletion ...

  4. Spark3.0YarnCluster模式任务提交流程源码分析

    1.通过spark-submit脚本提交spark程序 在spark-submit脚本里面执行了SparkSubmit类的main方法 2.运行SparkSubmit类的main方法 3.调用doSu ...

  5. JobTracker启动流程源码级分析

    org.apache.hadoop.mapred.JobTracker类是个独立的进程,有自己的main函数.JobTracker是在网络环境中提交及运行MR任务的核心位置. main方法主要代码有两 ...

  6. TaskTracker启动过程源码级分析

    TaskTracker也是作为一个单独的JVM来运行的,其main函数就是TaskTracker的入口函数,当运行start-all.sh时,脚本就是通过SSH运行该函数来启动TaskTracker的 ...

  7. MapReduce的MapTask任务的运行源码级分析

    TaskTracker任务初始化及启动task源码级分析 这篇文章中分析了任务的启动,每个task都会使用一个进程占用一个JVM来执行,org.apache.hadoop.mapred.Child方法 ...

  8. MapReduce job在JobTracker初始化源码级分析

    mapreduce job提交流程源码级分析(三)中已经说明用户最终调用JobTracker.submitJob方法来向JobTracker提交作业.而这个方法的核心提交方法是JobTracker.a ...

  9. 监听器初始化Job、JobTracker相应TaskTracker心跳、调度器分配task源码级分析

    JobTracker和TaskTracker分别启动之后(JobTracker启动流程源码级分析,TaskTracker启动过程源码级分析),taskTracker会通过心跳与JobTracker通信 ...

随机推荐

  1. 【BZOJ4898】[Apio2017]商旅 分数规划+SPFA

    [BZOJ4898][Apio2017]商旅 Description 在广阔的澳大利亚内陆地区长途跋涉后,你孤身一人带着一个背包来到了科巴.你被这个城市发达而美丽的市场所深深吸引,决定定居于此,做一个 ...

  2. mysql编译参数详解(./configure)

    1.--prefix=PREFIX:指定程序安装路径: 2.--enable-assembler:使用汇编模式:(文档说明:compiling in x86 (and sparc) versions  ...

  3. Spring整合Velocity模版引擎

    1. 首先通过pom.xml自动加载velocity扩展包到工程: <dependency> <groupId>velocity</groupId> <art ...

  4. javascript中字符串截取的两种方法

    var testStr = "hello kay!"; 1.substr testStr.substr(1)   ->ello kay! testStr.substr(1,4 ...

  5. 《挑战程序设计竞赛》2.1 深度优先搜索 POJ2386 POJ1979 AOJ0118 AOJ0033 POJ3009

    POJ2386 Lake Counting Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 25366   Accepted: ...

  6. coursera 《现代操作系统》 -- 第七周 存储模型(1)

    虚拟地址 隔离进程,便于管理. 问:为什么不直接划分物理地址为一块一块,直接管理,而要做一层虚拟地址的映射呢? 栈和堆 Differences between Stack and Heap Stack ...

  7. MySQL第二天

    回顾 数据库基础知识: 关系型数据库(磁盘)和非关系型数据库(内存)     关系型数据库: 建立在关系模型上的数据库 数据结构: 二维表(比较浪费空间) 操作数据的指令集合: SQL(DDL,DML ...

  8. Linux中的grep和cut

    提取行: grep --color  着色 -v         不包含 提取列: cut -f      列号 提取第几列 -d     分隔符 以什么为分隔符,默认是制表键 局限性:如果分隔符不那 ...

  9. swift笔记——环境搭建及Hello,Swift!

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/enson16855/article/details/29829601 首先要下载XCode6,仅仅有 ...

  10. 20170330 webservice代理类测试

    代理类测试 执行事物码SE80,找到之前创建好的代理类,如下图所示: 双击该代理类,进入其显示界面,如下图所示: 点击执行按钮,或者快捷键F8.如下图所示:. 逻辑端口文本框就是之前创建的逻辑端口技术 ...