Hadoop0.2之前版本和之後版本在Job中有很大的改進,本次採用的版本是Hadoop1.1.2版本。
現在作爲作業驅動器,可以直接繼承Configured以及實現Tool,這種方式可以很便捷的獲取啓動時候命令行中輸入的作業配置參數,常規的Job啓動如下:
public class SortByHash extends Configured implements Tool { public int run(String[] args) throws Exception { //這裏面負責配置job屬性 Configuration conf=getConf(); String[] paths=new GenericOptionsParser(conf, args).getRemainingArgs(); String tradeDir=paths[0]; String payDir=paths[1]; String joinDir=paths[2]; Job job=new Job(conf,"JoinJob"); job.setJarByClass(JoinMain.class); FileInputFormat.addInputPath(job, new Path(tradeDir)); FileInputFormat.addInputPath(job, new Path(payDir)); FileOutputFormat.setOutputPath(job, new Path(joinDir)); job.setMapperClass(JoinMapper.class); job.setReducerClass(JoinReducer.class); job.setMapOutputKeyClass(TextIntPair.class); job.setMapOutputValueClass(Text.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); job.waitForCompletion(true); int exitCode=job.isSuccessful()?0:1; return exitCode; } public static void main(String[] args) throws Exception { // TODO Auto-generated method stub int exitCode=ToolRunner.run(new SortByHashPartitioner(), args); System.exit(exitCode); } }
由於Tool的所有實現都需要實現Configurable,而Configured又是Configurable的具體實現,所以要同時繼承Configured和實現Tool,這樣就不需要實現Tool中定義的所有方法了。利用Tool接口來跑MapReduce,可以在命令行中設置一些參數,比硬編碼好很多。
注意利用Tool啓動作業基本方式如下:ToolRunner首先調用自己的靜態方法run,在該方法中會首先創建一個Configurable對象。然後調用GenericOptionsParser解析命令行參數,並設置給剛創建的Configurable對象。然後再次設置主類(這裏即SortByHashPartitioner)的setConf方法,最後調用主類的run方法執行。所以在run中要想使用命令行參數必須如下:
Configurationconf = getConf();
Jobjob = newJob(conf);
hadoop0.2之後的作業啓動是調用job.waitForCompletion(true);的;然後就會進行作業的提交、執行、完成等操作
調用waitForCompletion具體的工作流:
第一,調用submit提交作業
第二,當參數爲true的時候,調用monitorAndPrintJob來進行監聽作業的進度。
作業提交即submit():
第一,打開一個JobTracker的連接,這裏會創建一個JobClient對象
jobClient= new JobClient((JobConf) getConfiguration());
這裏的Configuration在初始化創建job的時候就會主動創建的
第二,根據創建的JobClient來調用submitJobInternal()提交作業給系統。
這裏會對於命令行中的選項進行檢查
1)獲取一個作業編號JobID,jobSubmitClient.getNewJobId(),jobSubmitclient是一個JobSubmissionProtocol,JobTracker就是這個類的子類,在JobClient創建的時候就會new一個
2)獲取目錄的代理,將運行作業需要的資源jar文件,配置文件都複製到一個以作業ID命名的目錄下JobTracker文件系統中。
3)檢查作業的輸出說明。如果沒有指定的輸出目錄或者輸出目錄已經不存在,則不提交,返回錯誤
4)創建作業的輸入分片。如果分片無法計算,如輸入路徑不存在,則不提交,報告錯誤。
5)將該作業寫入作業隊列中,然後將該文件寫入JobTracker的文件系統中。
6)所有都通過後,真正的提交作業,調用submitJob()告知JobTracker準備執行作業.
作業初始化
當JobTracker接受到submitJob()調用後,會將此調用放入內部隊列中queue,交由作業調度器(JobScheduler)進行調度,並對其進行初始化操作。
初始化主要是由作業調度器完成的,創建一個任務運行列表。作業調度器會首先從共享文件系統中獲取JobClient已經計算好的分片信息,然後爲每一個分片創建一個Map任務,創建的reduce數量由mapred.reduce.task來決定,一般是通過setNumReduceTask()設定的。
任務的分配
tasktracker會定期的向jobtracker發送一個心跳告訴是否存活,也是兩個之間的通信通道。這裏發送心跳的目的就是利用心跳來告知jobtracker,tasktracker還活着,會指明自己是否已經準備好運行新的任務,如果是,則jobtracker會爲它分配一個任務。這裏的tasktracker就利用週期性的循環來向jobtracker來“拉活”。
每一個tasktracker有固定的map任務槽和reduce任務槽。
選擇一個map任務,jobtracker會考慮tasktracker的網絡位置,並且選擇一個距離其輸入分片最近的tasktracker。一般都遵循數據本地化或機架本地化。
選擇一個reduce任務,jobtracker簡單地從待運行的reduce任務列表中選取下一個來執行,不許要考慮數據的本地化。
任務的執行
當tasktracker初次被分配了一個任務後,就開始要運行該任務。
第一步,從共享文件系統將作業的JAR文件複製到tasktracker所在的文件系統,目的就是實現了作業的JAR文件本地化。並且將應用程序所需要的全部文件從分佈式緩存中複製到本地磁盤。
第二步,tasktracker爲分配的任務創建一個本地工作目錄,將JAR文件內容解壓到這個文件夾
第三步,tasktracker創建一個TaskRunner實例來運行該任務。TaskRunner啓動一個新的JVM來運行每個任務。
任務進度的更新
monitorAndPrintJob(),這個方法就是實時的報告作業的運行情況,以打印在控制檯上。這個會每隔1秒進行查看,利用的就是Thread.sleep(1000)來執行的。
如果任務報告了進度,則會設置一個標誌來表明任務狀態發生了變化。在tasktracker中,除了運行任務的線程外,還有個獨立的線程每隔3秒會檢測任務的狀態,如果已經設置,則告知tasktracker當前任務狀態。而tasktracker每隔5秒會發送心跳到jobtracker,這裏發送心跳的目的主要是報告tasktracker上運行的所有任務的狀態。
作業的完成
當jobtracker收到作業的最後一個任務已經完成的通知後,則就會把作業的狀態設置爲“成功”。此時JobClient會打印一條消息告知用戶作業已經完成了。Jobtracker和tasktracker都會清空作業的工作狀態。