以下是我研究mapreduce源碼的幾個步驟,其中沒有涉及太多的細節問題,主要就是先將思路理清,爲以後繼續深入研究做點筆記,主要涉及到map和reduce任務的執行步驟,首先從JobTracler的啓動開始,之後到TaskTracker的啓動,TaskTracker通過心跳機制再次回到JobTracker,然後再看JobTracker是怎樣回覆心跳的,然後又會有TaskTracker接受HeartBeatResponse的一系列動作,最後很自然就看到了map和reduce任務是怎樣執行的,最後回到mapreduce包中,mapreduce包中會有一些抽象類以及具體實現的類(在mapreduce.lib中),大概就是這樣一個思路,能理清這樣的思路要感謝豆丁網的一位網友的博文(我再次找的時候已經找不到了)以及“覺先”的博文!
一.JobTracker的啓動:
JobTracker裏面裏面有一個main方法,調試時用的,真正應用中在namenode中會啓動該方法,main方法裏面調用兩個方法,分別是StartTracker和OfferSrervice,第一個方法是調用JobTracker的構造函數生成它的對象,並進行一些初始化,包括啓動RPCSERVER,內置的jetty服務器,是否重啓JobTracker等,第二個方法調用了TaskScheduler的start方法,TaskScheduler是一個抽象類,由JobQueueTaskScheduler具體來實現它,所以真正調用的是JobQueueTaskScheduler的start方法,下面來分析這個start方法來帶的一系列效應:
該方法主要是註冊了兩個很重要的監聽器,JobQueueJobInProgressListener和eagerTaskIntinilionListener,它們都是extends自JobInProgressListener,代碼如下:
----------------------------------------------------------------------------------------------------------------------
@Override
publicsynchronizedvoidstart()throwsIOException{
super.start();
taskTrackerManager.addJobInProgressListener(jobQueueJobInProgressListener);
eagerTaskInitializationListener.setTaskTrackerManager(taskTrackerManager);
eagerTaskInitializationListener.start();
taskTrackerManager.addJobInProgressListener(
eagerTaskInitializationListener);
}
----------------------------------------------------------------------------------------------------------------------
第一個監聽器是以FIFO的方式來維護一個JobInProgress隊列的,並且監聽各個JobInProgress實例的生命週期(之後再好好研究。。。),第二個eagerTaskIntinilionListener是EagerTaskIntinilionListener的一個實例,jobInitQueue主要是在這個類裏面生成,它是一個arraylist,所以EagerTaskIntinilionListener是用來監聽jobInitQueue,一旦有新的job提交,也就是有JobInProgress的實例加入,就會觸發JobInProgress實例的initTask方法,對job進行初始化。這個initTask方法比較複雜,我先按照一條軸線來分析,其他的代碼之後再研究,代碼如下:
----------------------------------------------------------------------------------------------------------------------
publicsynchronizedvoidinitTasks()
throwsIOException,KillInterruptedException{
。。。
。。。
。。。
//readinputsplitsandcreateamapperasplit
//從HDFS中讀取job.spilt文件從而生成inputspilts
StringjobFile=profile.getJobFile();
PathsysDir=newPath(this.jobtracker.getSystemDir());
FileSystemfs=sysDir.getFileSystem(conf);
DataInputStream splitFile =
fs.open(newPath(conf.get("mapred.job.split.file")));
JobClient.RawSplit[] splits;
try{
splits =JobClient.readSplitFile(splitFile);
}finally{
splitFile.close();
}
//map的個數就是split的個數
numMapTasks=splits.length;
//ifthenumberofsplitsislargerthanaconfiguredvalue
//thenfailthejob.
intmaxTasks=jobtracker.getMaxTasksPerJob();
if(maxTasks>0&&numMapTasks+numReduceTasks>maxTasks){
thrownewIOException(
"Thenumberoftasksforthisjob"+
(numMapTasks+numReduceTasks)+
"exceedstheconfiguredlimit"+maxTasks);
}
jobtracker.getInstrumentation().addWaiting(
getJobID(),numMapTasks+numReduceTasks);
//爲每一個maptask生成一個TaskInProgress來處理
maps=newTaskInProgress[numMapTasks];
for(inti=0;i<numMapTasks;++i){
inputLength+=splits[i].getDataLength();
maps[i]=newTaskInProgress(jobId,jobFile,
splits[i],
jobtracker,conf,this,i);
}
LOG.info("Inputsizeforjob"+jobId+"="+inputLength
+".Numberofsplits="+splits.length);
if(numMapTasks>0){
nonRunningMapCache=createCache(splits,maxLevel);
}
}
----------------------------------------------------------------------------------------------------------------------
對於maptask,將其放入nonRunningMapCache裏面,他是一個Map<Node,List<TaskInProgress>>,對於maptask 來講,他會被分配到inputsplit所在的Node上,Node在此表示DataNode或者機架或者數據中心,nonRunningMapCache將在Jobtracker向TaskTracker分配maptask的時候用到。。。
----------------------------------------------------------------------------------------------------------------------
nonRunningMapCache=createCache(splits,maxLevel);
----------------------------------------------------------------------------------------------------------------------
然後再創建reducetask,放入nonRunningReduces裏面,將在Jobtracker想TaskTracker分配reducetask的時候用到。。。
-----------------------------------------------------------------------------------------
this.reduces=newTaskInProgress[numReduceTasks];
for(inti=0;i<numReduceTasks;i++){
reduces[i]=newTaskInProgress(jobId,jobFile,
numMapTasks,i,
jobtracker,conf,this);
nonRunningReduces.add(reduces[i]);
}
-----------------------------------------------------------------------------------------
創建兩個cleanuptask,用來清理map和reduce
-----------------------------------------------------------------------------------------
//create cleanup two cleanup tips, one map and one reduce.
cleanup=newTaskInProgress[2];
//cleanupmaptip.Thismapdoesn'tuseanysplits.Justassignanempty
//split.
JobClient.RawSplitemptySplit=newJobClient.RawSplit();
cleanup[0]=newTaskInProgress(jobId,jobFile,emptySplit,
jobtracker,conf,this,numMapTasks);
cleanup[0].setJobCleanupTask();
//cleanupreducetip.
cleanup[1]=newTaskInProgress(jobId,jobFile,numMapTasks,
numReduceTasks,jobtracker,conf,this);
cleanup[1].setJobCleanupTask();
-----------------------------------------------------------------------------------------
創建兩個初始化task,一個初始化map,一個初始化reduce
-----------------------------------------------------------------------------------------
//create two setup tips, one map and one reduce.
setup=newTaskInProgress[2];
//setupmaptip.Thismapdoesn'tuseanysplit.Justassignanempty
//split.
setup[0]=newTaskInProgress(jobId,jobFile,emptySplit,
jobtracker,conf,this,numMapTasks+1);
setup[0].setJobSetupTask();
//setupreducetip.
setup[1]=newTaskInProgress(jobId,jobFile,numMapTasks,
numReduceTasks+1,jobtracker,conf,this);
setup[1].setJobSetupTask();
最後看到tasksInited.set(true)方法,說明初始化完成,其他方法之後再看。
----------------------------------------------------------------------------------------------------------------------
synchronized(jobInitKillStatus){
jobInitKillStatus.initDone=true;
if(jobInitKillStatus.killed){
thrownewKillInterruptedException("Job"+jobId+"killedininit");
}
}
tasksInited.set(true);
JobHistory.JobInfo.logInited(profile.getJobID(),this.launchTime,
numMapTasks,numReduceTasks);