本篇主要介绍Job从客户端提交到JobTracker及其被初始化的过程。
以WordCount为例,以前的程序都是通过JobClient.runJob()方法来提交Job,但是现在大多用Job.waitForCompletion(true)方法来提交(true表示打印出运行过程),但其本质都是一样的,最终都是通过JobClient的submitJobInternal()方法来提交Job。
1 public
2 RunningJob submitJobInternal(final JobConf job
3 ) throws FileNotFoundException,
4 ClassNotFoundException,
5 InterruptedException,
6 IOException {
7 ......
8 //为job获取id
9 JobID jobId = jobSubmitClient.getNewJobId();
10 Path submitJobDir = new Path(jobStagingArea, jobId.toString());
11 jobCopy.set("mapreduce.job.dir", submitJobDir.toString());
12
13 ......
14
15 printTokens(jobId, jobCopy.getCredentials());
16 status = jobSubmitClient.submitJob(
17 jobId, submitJobDir.toString(), jobCopy.getCredentials());
18 ......
19 }
submitJobInternal()方法主要完成这么几个工作:得到授权令牌;检查输出目录是否已存在;创建分片;将运行作业所需的资源复制到JobTracker的文件系统中。最后调用JobSubmissionProtocol的submitJob()方法。JobTracker继承了JobSubmissionProtocol接口,所以会转到去调用JobTracker的submitJob()方法。
这里插一句,JobSubmissionProtocol接口有两个默认的子类实现:JobTracker和LocalJobRunner。如果使用的是hadoop的默认配置,在mapred-site.xml文件中{mapred.job.tracker}的值为“local”,此时JobSubmissionProtocol的实现使用LocalJobRunner,即使用的是本地文件系统。否则的话使用HDFS。这也是为什么我们在mapred-site.xml文件要配置{mapred.job.tracker}的原因。具体使用哪个JobSubmissionProtocol是在JobClient初始化的时候决定的。从下面JobClient的init()方法代码可以清晰的看到:
1 public void init(JobConf conf) throws IOException {
2 String tracker = conf.get("mapred.job.tracker", "local");
3 tasklogtimeout = conf.getInt(
4 TASKLOG_PULL_TIMEOUT_KEY, DEFAULT_TASKLOG_TIMEOUT);
5 this.ugi = UserGroupInformation.getCurrentUser();
6 if ("local".equals(tracker)) {
7 conf.setNumMapTasks(1);
8 this.jobSubmitClient = new LocalJobRunner(conf);
9 } else {
10 this.rpcJobSubmitClient =
11 createRPCProxy(JobTracker.getAddress(conf), conf);
12 this.jobSubmitClient = createProxy(this.rpcJobSubmitClient, conf);
13 }
14 }
接着上面来说。看看JobTracker的submit()方法。
1 JobStatus submitJob(JobID jobId, String jobSubmitDir,
2 UserGroupInformation ugi, Credentials ts, boolean recovered)
3 throws IOException {
4 // Check for safe-mode
5 checkSafeMode();
6 ......
7 JobInProgress job = null;
8
9 // Submit the job
10 JobStatus status;
11 try {
12 status = addJob(jobId, job);
13 } catch (IOException ioe) {
14 LOG.info("Job " + jobId + " submission failed!", ioe);
15 status = job.getStatus();
16 status.setFailureInfo(StringUtils.stringifyException(ioe));
17 failJob(job);
18 throw ioe;
19 }
20 return status;
21 }
22 }
首先检查系统是否处于安全模式。接着会创建JobInProgress对象,这个对象用来维护了Job运行的相关信息。然后来检查用户的队列权限,并检查内存的使用情况。最终调用addJob()方法来提交job。
1 synchronized (jobs) {
2 synchronized (taskScheduler) {
3 jobs.put(job.getProfile().getJobID(), job);
4 for (JobInProgressListener listener : jobInProgressListeners) {
5 listener.jobAdded(job);
6 }
7 }
8 }
这里用到了观察者模式,jobInProgressListeners是一个List<JobInProgressListener>,代表所有已注册的监听器(观察者)。listener.jobAdded(job);这行语句则分别调用所有已注册listener的jobAdded()方法。从上一篇文章中我们知道,最主要的listener就是EagerTaskInitializationListener和JobQueueJobInProgressListener。
JobQueueJobInProgressListener的jobAdded()方法比较简单,只有一句话,就是先构建一个JobSchedulingInfo对象,然后和JobInProgress对应起来放入jobQueue中。
下面是EagerTaskInitializationListener的jobAdded()方法:
1 @Override
2 public void jobAdded(JobInProgress job) {
3 synchronized (jobInitQueue) {
4 jobInitQueue.add(job);
5 resortInitQueue();
6 jobInitQueue.notifyAll();
7 }
8 }
这个方法首先将job(JobInProgress)添加到初始化队列中;然后按优先级对队列中的JobInProcess进行排序。上篇文件中介绍了,在EagerTaskInitializationListener中监听到有新的job(JobInProgress)添加到队列中时,则会对其进行初始化工作。最终是调用了JobTracker的initJob()方法来对job进行初始化,这部分过程在下一篇文章再写吧。
最后画个流程图来总结一下,画的不好,将就看一下吧。
本文基于hadoop1.2.1
如有错误,还请指正
参考文章:《Hadoop技术内幕 深入理解MapReduce架构设计与实现原理》 董西成