MapReduce job在JobTracker初始化原始碼級分析

玖瘋發表於2014-05-22

  mapreduce job提交流程原始碼級分析(三)中已經說明使用者最終呼叫JobTracker.submitJob方法來向JobTracker提交作業。而這個方法的核心提交方法是JobTracker.addJob(JobID jobId, JobInProgress job)方法,這個addJob方法會把Job提交到排程器(預設是JobQueueTaskScheduler)的監聽器JobQueueJobInProgressListener和EagerTaskInitializationListener(本文只討論預設排程器)中,使用方法jobAdded(JobInProgress job),JobQueueJobInProgressListener任務是監控各個JobInProcess生命週期中的變化;EagerTaskInitializationListener是發現有新Job後對其初始化的。

  一、JobQueueJobInProgressListener.jobAdded(JobInProgress job)方法。就一句程式碼jobQueue.put(new JobSchedulingInfo(job.getStatus()), job),先構建一個JobSchedulingInfo物件,然後和JobInProgress對應起來放入jobQueue中。JobSchedulingInfo類維護這排程這個job必備的一些資訊,比如優先順序(預設是NORMAL)、JobID以及開始時間startTime。 

  二、EagerTaskInitializationListener.jobAdded(JobInProgress job)方法。  

 1 /**
 2    * We add the JIP to the jobInitQueue, which is processed 
 3    * asynchronously to handle split-computation and build up
 4    * the right TaskTracker/Block mapping.
 5    */
 6   @Override
 7   public void jobAdded(JobInProgress job) {
 8     synchronized (jobInitQueue) {
 9       jobInitQueue.add(job);  //新增進List<JobInProgress> jobInitQueue
10       resortInitQueue();
11       jobInitQueue.notifyAll();  //喚醒阻塞的程式
12     }
13 
14   }

  上面方法中resortInitQueue()方法主要是對jobInitQueue中JobInProcess進行排序,先按照優先順序排序,相同的再按開始時間。EagerTaskInitializationListener.start()在排程器初始化時JobQueueTaskScheduler.start()就呼叫了,所以先於jobAdded方法呼叫。EagerTaskInitializationListener.start()程式碼如下:

1 public void start() throws IOException {
2     this.jobInitManagerThread = new Thread(jobInitManager, "jobInitManager");
3     jobInitManagerThread.setDaemon(true);
4     this.jobInitManagerThread.start();
5   }

  start()方法會啟動一個執行緒:JobInitManager。

 1 /////////////////////////////////////////////////////////////////
 2   //  Used to init new jobs that have just been created
 3   /////////////////////////////////////////////////////////////////
 4   class JobInitManager implements Runnable {
 5    
 6     public void run() {
 7       JobInProgress job = null;
 8       while (true) {
 9         try {
10           synchronized (jobInitQueue) {
11             while (jobInitQueue.isEmpty()) {
12               jobInitQueue.wait();
13             }
14             job = jobInitQueue.remove(0);
15           }
16           threadPool.execute(new InitJob(job));
17         } catch (InterruptedException t) {
18           LOG.info("JobInitManagerThread interrupted.");
19           break;
20         } 
21       }
22       LOG.info("Shutting down thread pool");
23       threadPool.shutdownNow();
24     }
25   }
26   
27   class InitJob implements Runnable {
28   
29     private JobInProgress job;
30     
31     public InitJob(JobInProgress job) {
32       this.job = job;
33     }
34     
35     public void run() {
36       ttm.initJob(job);//對應JobTracker的對應方法
37     }
38   }

  JobInitManager執行緒的run方法是一個死迴圈始終監控jobInitQueue是否為空,不為空的話就取出0位置的JobInProgress,在InitJob執行緒中初始化:TaskTrackerManager.initJob(job)對應JobTracker的initJob方法。這裡為什麼會另起執行緒來初始化Job呢?原因很簡單,就是可能jobInitQueue中同時會有很多JobInProgress,一個一個的初始化會比較慢,所以採用多執行緒的方式初始化。來看initJob方法的程式碼:

 1   public void initJob(JobInProgress job) {
 2     if (null == job) {
 3       LOG.info("Init on null job is not valid");
 4       return;
 5     }
 6             
 7     try {
 8       JobStatus prevStatus = (JobStatus)job.getStatus().clone();
 9       LOG.info("Initializing " + job.getJobID());
10       job.initTasks();    //呼叫該例項的initTasks方 法,對job進行初始化
11       // Inform the listeners if the job state has changed
12       // Note : that the job will be in PREP state.
13       JobStatus newStatus = (JobStatus)job.getStatus().clone();
14       if (prevStatus.getRunState() != newStatus.getRunState()) {
15         JobStatusChangeEvent event = 
16           new JobStatusChangeEvent(job, EventType.RUN_STATE_CHANGED, prevStatus, 
17               newStatus);
18         synchronized (JobTracker.this) {
19           updateJobInProgressListeners(event);
20         }
21       }
22     } catch (KillInterruptedException kie) {
23       //   If job was killed during initialization, job state will be KILLED
24       LOG.error("Job initialization interrupted:\n" +
25           StringUtils.stringifyException(kie));
26       killJob(job);
27     } catch (Throwable t) {
28       String failureInfo = 
29         "Job initialization failed:\n" + StringUtils.stringifyException(t);
30       // If the job initialization is failed, job state will be FAILED
31       LOG.error(failureInfo);
32       job.getStatus().setFailureInfo(failureInfo);
33       failJob(job);
34     }
35      }

  首先是獲取初始化前的狀態prevStatus;然後是job.initTasks()初始化;在獲取初始化的後的狀態newStatus;

  job.initTasks()方法程式碼比較多,主要的工作是檢查之後獲取輸入資料的分片資訊TaskSplitMetaInfo[] splits = createSplits(jobId)這是去讀的上傳到HDFS中的檔案job.splitmetainfo和job.split,要確保numMapTasks == splits.length,然後構建numMapTasks個TaskInProgress作為MapTask,

相關文章