引文
學習Hadoop的同學們,一定知道如果運行Hadoop自帶的各種例子,以大名鼎鼎的wordcount為例,你會輸入以下命令:
hadoop org.apache.hadoop.examples.WordCount -D mapreduce.input.fileinputformat.split.maxsize=1 /wordcount/input /wordcount/output/result1
當然,有些人還會用以下替代方式:
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.0.jar wordcount /wordcount/input /wordcount/output/result1
相比於原始的執行方式,使用jar命令方式,讓我們不用再敲入繁瑣的完整包路徑。比如我們知道hadoop-mapreduce-examples項目中還提供了其它的例子,比如計算圓周率的例子,我們只需要記住此應用的簡單名字pi,就可以執行它:
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.0.jar pi 5 10
雖說我們只是使用這些現成的例子,沒有必要較真,但是這種簡潔的使用方式,無疑還是值得借鑒的。本文將分析下這種方式實現的原理,有興趣的同學可以一讀。
源碼分析
這一節,我們通過對hadoop-mapreduce-examples項目中的關鍵源碼進行分析,理解簡潔執行的原理。在hadoop-mapreduce-examples項目的pom.xml文件中配置了org.apache.hadoop.examples.ExampleDriver作為jar命令的入口,配置如下:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>org.apache.hadoop.examples.ExampleDriver</mainClass> </manifest> </archive> </configuration> </plugin>
這決定了使用jar命令執行hadoop-mapreduce-examples-2.6.0.jar包時,實際執行了ExampleDriver的main方法,ExampleDriver的實現如下:
public class ExampleDriver { public static void main(String argv[]){ int exitCode = -1; ProgramDriver pgd = new ProgramDriver(); try { pgd.addClass("wordcount", WordCount.class, "A map/reduce program that counts the words in the input files."); // 省略其它例子的注冊代碼 pgd.addClass("pi", QuasiMonteCarlo.class, QuasiMonteCarlo.DESCRIPTION); // 省略其它例子的注冊代碼 exitCode = pgd.run(argv); } catch(Throwable e){ e.printStackTrace(); } System.exit(exitCode); } }
以上代碼構造了ProgramDriver的實例,並且調用其addClass方法,三個參數分別是例子名稱(如wordcount、pi等)、例子的實現Class、例子的描述信息。ProgramDriver的addClass方法的實現如下:
public void addClass(String name, Class<?> mainClass, String description) throws Throwable { programs.put(name , new ProgramDescription(mainClass, description)); }
首先,構造ProgramDescription對象,其構造函數如下:
public ProgramDescription(Class<?> mainClass, String description) throws SecurityException, NoSuchMethodException { this.main = mainClass.getMethod("main", paramTypes); this.description = description; }
其中main的類型是java.lang.reflect.Method,用於保存例子Class的main方法。
然后,將例子名稱(如wordcount、pi等)和ProgramDescription實例注冊到programs中,programs的類型定義如下:
/** * A description of a program based on its class and a * human-readable description. */ Map<String, ProgramDescription> programs;
ExampleDriver的main方法在最后會調用ProgramDriver的run方法,其實現如下:
public int run(String[] args) throws Throwable { // Make sure they gave us a program name. if (args.length == 0) { System.out.println("An example program must be given as the" + " first argument."); printUsage(programs); return -1; } // And that it is good. ProgramDescription pgm = programs.get(args[0]); if (pgm == null) { System.out.println("Unknown program '" + args[0] + "' chosen."); printUsage(programs); return -1; } // Remove the leading argument and call main String[] new_args = new String[args.length - 1]; for(int i=1; i < args.length; ++i) { new_args[i-1] = args[i]; } pgm.invoke(new_args); return 0; }
ProgramDriver的run方法執行的步驟如下:
1. 參數長度校驗;
2. 根據第一個參數,從programs中查找對應的ProgramDescription實例;
3. 將其余的參數傳遞給ProgramDescription的invoke方法,進而執行對應的例子。
ProgramDescription的invoke方法的實現如下:
public void invoke(String[] args) throws Throwable { try { main.invoke(null, new Object[]{args}); } catch (InvocationTargetException except) { throw except.getCause(); } }
由此我們知道具體例子的執行,是通過反射調用具體例子Class的main方法,最終實現的。
后記:個人總結整理的《深入理解Spark:核心思想與源碼分析》一書現在已經正式出版上市,目前京東、當當、天貓等網站均有銷售,歡迎感興趣的同學購買。
京東:http://item.jd.com/11846120.html
當當:http://product.dangdang.com/23838168.html