[Hadoop源碼解讀](三)MapReduce篇之Job類

  下面,我們只涉及MapReduce 1,而不涉及YARN。



                                          

  當我們在寫MapReduce程序的時候,通常,在main函數裏,我們會像下面這樣做。建立一個Job對象,設置它的JobName,然後配置輸入輸出路徑,設置我們的Mapper類和Reducer類,設置InputFormat和正確的輸出類型等等。然後我們會使用job.waitForCompletion()提交到JobTracker,等待job運行並返回,這就是一般的Job設置過程。JobTracker會初始化這個Job,獲取輸入分片,然後將一個一個的task任務分配給TaskTrackers執行。TaskTracker獲取task是通過心跳的返回值得到的,然後TaskTracker就會爲收到的task啓動一個JVM來運行。

  

        Configuration conf = getConf();  
        Job job = new Job(conf, "SelectGradeDriver");  
        job.setJarByClass(SelectGradeDriver.class);   
          
        Path in = new Path(args[0]);  
        Path out = new Path(args[1]);  
          
        FileInputFormat.setInputPaths(job, in);  
        FileOutputFormat.setOutputPath(job, out);  
          
        job.setMapperClass(SelectGradeMapper.class);  
        job.setReducerClass(SelectGradeReducer.class);  
          
        job.setInputFormatClass(TextInputFormat.class);  
        job.setOutputFormatClass(TextOutputFormat.class);  
          
        job.setMapOutputKeyClass(InstituteAndGradeWritable.class);  
        job.setMapOutputValueClass(Text.class);  
          
        job.setOutputKeyClass(InstituteAndGradeWritable.class);  
        job.setOutputValueClass(Text.class);  
          
        System.exit(job.waitForCompletion(true)? 0 : 1);  

  Job其實就是提供配置作業、獲取作業配置、以及提交作業的功能,以及跟蹤作業進度和控製作業。Job類繼承於JobContext類。JobContext提供了獲取作業配置的功能,如作業ID,作業的Mapper類,Reducer類,輸入格式,輸出格式等等,它們除了作業ID之外,都是隻讀的。 Job類在JobContext的基礎上,提供了設置作業配置信息的功能、跟蹤進度,以及提交作業的接口和控製作業的方法。

public class Job extends JobContext {  
  public static enum JobState {DEFINE, RUNNING};
  private JobState state = JobState.DEFINE;
  private JobClient jobClient;
  private RunningJob info;
  public float setupProgress() throws IOException {
    ensureState(JobState.RUNNING);
    return info.setupProgress();
  }


  public float mapProgress() throws IOException {
    ensureState(JobState.RUNNING);
    return info.mapProgress();
  }
  public float reduceProgress() throws IOException {
    ensureState(JobState.RUNNING);
    return info.reduceProgress();
  }
  public boolean isComplete() throws IOException {
    ensureState(JobState.RUNNING);
    return info.isComplete();
  }
  public boolean isSuccessful() throws IOException {
    ensureState(JobState.RUNNING);
    return info.isSuccessful();
  }
  public void killJob() throws IOException {
    ensureState(JobState.RUNNING);
    info.killJob();
  }
 public TaskCompletionEvent[] getTaskCompletionEvents(int startFrom
                                                       ) throws IOException {
    ensureState(JobState.RUNNING);
    return info.getTaskCompletionEvents(startFrom);
  }

  public void killTask(TaskAttemptID taskId) throws IOException {
    ensureState(JobState.RUNNING);
    info.killTask(org.apache.hadoop.mapred.TaskAttemptID.downgrade(taskId), 
                  false);
  }
  public void failTask(TaskAttemptID taskId) throws IOException {
    ensureState(JobState.RUNNING);
    info.killTask(org.apache.hadoop.mapred.TaskAttemptID.downgrade(taskId), 
                  true);
  }

  public Counters getCounters() throws IOException {
    ensureState(JobState.RUNNING);
    return new Counters(info.getCounters());
  }
  public void submit() throws IOException, InterruptedException, 
                              ClassNotFoundException {
    ensureState(JobState.DEFINE);
    setUseNewAPI();
    
    // Connect to the JobTracker and submit the job
    connect();
    info = jobClient.submitJobInternal(conf);
    super.setJobID(info.getID());
    state = JobState.RUNNING;
   }
  private void connect() throws IOException, InterruptedException {
    ugi.doAs(new PrivilegedExceptionAction<Object>() {
      public Object run() throws IOException {
        jobClient = new JobClient((JobConf) getConfiguration());    
        return null;
      }
    });
  }
  public boolean waitForCompletion(boolean verbose
                                   ) throws IOException, InterruptedException,
                                            ClassNotFoundException {
    if (state == JobState.DEFINE) {
      submit();
    }
    if (verbose) {
      jobClient.monitorAndPrintJob(conf, info);
    } else {
      info.waitForCompletion();
    }
    return isSuccessful();
  }
  //lots of setters and others
}

  一個Job對象有兩種狀態,DEFINE和RUNNING,Job對象被創建時的狀態時DEFINE,當且僅當Job對象處於DEFINE狀態,纔可以用來設置作業的一些配置,如Reduce task的數量、InputFormat類、工作的Mapper類,Partitioner類等等,這些設置是通過設置配置信息conf來實現的;當作業通過submit()被提交,就會將這個Job對象的狀態設置爲RUNNING,這時候作業以及提交了,就不能再設置上面那些參數了,作業處於調度運行階段。處於RUNNING狀態的作業我們可以獲取作業、map task和reduce task的進度,通過代碼中的*Progress()獲得,這些函數是通過info來獲取的,info是RunningJob對象,它是實際在運行的作業的一組獲取作業情況的接口,如Progress。

  在waitForCompletion()中,首先用submit()提交作業,然後等待info.waitForCompletion()返回作業執行完畢。verbose參數用來決定是否將運行進度等信息輸出給用戶。submit()首先會檢查是否正確使用了new API,這通過setUseNewAPI()檢查舊版本的屬性是否被設置來實現的[設置是否使用newAPI是因爲執行Task時要根據使用的API版本來執行不同版本的MapReduce,在後面講MapTask時會說到],接着就connect()連接JobTracker並提交。實際提交作業的是一個JobClient對象,提交作業後返回一個RunningJob對象,這個對象可以跟蹤作業的進度以及含有由JobTracker設置的作業ID。

  getCounter()函數是用來返回這個作業的計數器列表的,計數器被用來收集作業的統計信息,比如失敗的map task數量,reduce輸出的記錄數等等。它包括內置計數器和用戶定義的計數器,用戶自定義的計數器可以用來收集用戶需要的特定信息。計數器首先被每個task定期傳輸到TaskTracker,最後TaskTracker再傳到JobTracker收集起來。這就意味着,計數器是全局的。

  關於Counter相關的類,爲了保持篇幅簡短,放在下一篇講。


  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章