mapreduce job提交流程原始碼級分析(二)(原創)這篇文章說到了jobSubmitClient.submitJob(jobId, submitJobDir.toString(), jobCopy.getCredentials())提交job,最終呼叫的是JobTracker.submitJob;而這篇文章JobTracker啟動流程原始碼級分析則是分析的JobTracker的啟動過程,JobTracker啟動之後就會等待提交作業管理作業等。
接下來看看JobTracker.submitJob方法,呼叫這個方法之前已經將相關的資源分片資訊、配置資訊、外部檔案、第三方jar包、一些歸檔檔案以及job.jar上傳到HDFS中了。
1 public JobStatus submitJob(JobID jobId, String jobSubmitDir, Credentials ts) 2 throws IOException { 3 JobInfo jobInfo = null; 4 UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); 5 synchronized (this) { 6 if (jobs.containsKey(jobId)) { 7 // job already running, don't start twice 8 return jobs.get(jobId).getStatus(); 9 } 10 jobInfo = new JobInfo(jobId, new Text(ugi.getShortUserName()), 11 new Path(jobSubmitDir)); 12 } 13 14 // Create the JobInProgress, do not lock the JobTracker since 15 // we are about to copy job.xml from HDFS 16 //當JobTracker接收到新的job請求(即submitJob()函式被呼叫)後, 17 //會建立一個JobInProgress物件並通過它來管理和排程任務。 18 //JobInProgress在建立的時候會初始化一系列與任務有關的引數,呼叫到FileSystem, 19 //把在JobClient端上傳的所有任務檔案下載到本地的檔案系統中的臨時目錄裡。這其中包括上傳的*.jar檔案包、 20 //記錄配置資訊的xml、記錄分割資訊的檔案。 21 JobInProgress job = null; 22 try { 23 job = new JobInProgress(this, this.conf, jobInfo, 0, ts); 24 } catch (Exception e) { 25 throw new IOException(e); 26 } 27 28 synchronized (this) { 29 // check if queue is RUNNING 30 String queue = job.getProfile().getQueueName(); 31 if (!queueManager.isRunning(queue)) { 32 throw new IOException("Queue \"" + queue + "\" is not running"); 33 } 34 try { 35 aclsManager.checkAccess(job, ugi, Operation.SUBMIT_JOB); 36 } catch (IOException ioe) { 37 LOG.warn("Access denied for user " + job.getJobConf().getUser() 38 + ". Ignoring job " + jobId, ioe); 39 job.fail(); 40 throw ioe; 41 } 42 43 // Check the job if it cannot run in the cluster because of invalid memory 44 // requirements. 45 try { 46 checkMemoryRequirements(job); 47 } catch (IOException ioe) { 48 throw ioe; 49 } 50 boolean recovered = true; // TODO: Once the Job recovery code is there, 51 // (MAPREDUCE-873) we 52 // must pass the "recovered" flag accurately. 53 // This is handled in the trunk/0.22 54 if (!recovered) { 55 // Store the information in a file so that the job can be recovered 56 // later (if at all) 57 Path jobDir = getSystemDirectoryForJob(jobId); 58 FileSystem.mkdirs(fs, jobDir, new FsPermission(SYSTEM_DIR_PERMISSION)); 59 FSDataOutputStream out = fs.create(getSystemFileForJob(jobId)); 60 jobInfo.write(out); 61 out.close(); 62 } 63 64 // Submit the job 65 JobStatus status; 66 try { 67 status = addJob(jobId, job); 68 } catch (IOException ioe) { 69 LOG.info("Job " + jobId + " submission failed!", ioe); 70 status = job.getStatus(); 71 status.setFailureInfo(StringUtils.stringifyException(ioe)); 72 failJob(job); 73 throw ioe; 74 } 75 76 return status; 77 } 78 }
一、首先看看jobs中有無要提交的Job,jobs是一個Map<JobID, JobInProgress> 這裡儲存著所有已知的Job及其對應的JobInProgress資訊。如果已經存在這個Job則直接返回這個Job的狀態;如果不存在則利用JobID和jobSubmitDir構造一個JobInfo物件,JobInfo類實現了Writable可以被序列化,而且儲存三個欄位JobID、user、以及上傳資源的目錄jobSubmitDir;
二、建立一個JobInProgress物件,JobInProgress類主要用於監控和跟蹤作業執行狀態,存在於作業的整個執行過程中,併為排程器提供最底層的排程介面,維護了兩部分資訊:一種是靜態資訊這些在作業提交之時就確定好了;另一種是動態的會隨著作業的執行而動態變化的。job = new JobInProgress(this, this.conf, jobInfo, 0, ts),這裡會建立一個JobProfile一直跟蹤作業的執行,不管作業作業活著還是死了;
三、checkMemoryRequirements(job)檢查Job是否有無效的記憶體需求而不能執行,檢查JobTracker的配置有無問題,再檢查Job的記憶體配置有無問題;
四、是否儲存作業資訊以備恢復。在1.0.0版本中這還沒實現(在這就是沒儲存資訊),要存的資訊是一個JobInfo物件儲存著作業的儲存目錄、ID以及user。
五、status = addJob(jobId, job)這是核心的提交方法。會將此Job放入jobs中,jobs儲存著JobTracker所有執行作業的對應關係<jobID,JobInProgress>;然後讓所有的JobInProgressListener監聽這個Job,根據JobTracker啟動流程原始碼級分析 中可以知道這些JobInProgressListener例項都是通過排程器初始化(JobQueueTaskScheduler.start()方法)時,有倆執行緒一個是監控Job生命週期的,一個是對新加入的Job初始化的;一個監控Job的整個生命週期;然後加入監控統計中,返回job狀態job.getStatus()。
這樣Job的提交過程就完了,剩下的就是作業的排程分配及監控了。後續再講吧
參考:
董西成,《Hadoop技術內幕:深入解析MapReduce架構設計與實現原理》