在Hadoop的发行包中也附带了例子的源代码,WordCount.java类的主函数实现如下所示:


public 
   
 static 
   
 void 
  main(String[] args)  
 throws 
  Exception {
      
 int 
  res  
 = 
  ToolRunner.run( 
 new 
  Configuration(),  
 new 
  WordCount(), args);
     System.exit(res);
 }
 }


我们先从主函数入手吧,一点点地按照“深度遍历”的思想,分解掉这个WordCount字频统计工具,让我们更清晰地看到到底在Hadoop中是如何进行工作的。

首先从ToolRunner的run方法开始,run方法需要三个参数,第一个是一个Configuration类的实例。第二个是 WorCount类的实例,args就是从控制台接收到的命令行数组。可见,估计分析到我们的WordCount还非常非常的远,因为 Configuration类与args数组就够追踪一会了。

下面是ToolRunner的run方法的实现:


 

public 
   
 static 
   
 int 
  run(Configuration conf, Tool tool, String[] args) 
      
 throws 
  Exception{
      
 if 
 (conf  
 == 
   
 null 
 ) {  
 // 
  即使传入的conf为null,仍然会在这里实例化一个配置类Configuration的对象 
 
 
       conf  
 = 
   
 new 
  Configuration();
     }
     GenericOptionsParser parser  
 = 
   
 new 
  GenericOptionsParser(conf, args);  
 // 
  根据指定的conf和args数组实例化一个GenericOptionsParser类的对象,构造GenericOptionsParser类对象能实现对Hadoop通用的配置信息进行解析
      
 // 
  Tool类是一个接口,WordCount工具就是实现了Tool接口,Tool接口中只是定义了一个run方法,即实现一个Tool必须要知道这个Tool的实现类的对象怎样run。
      
 // 
  因为Tool接口实现了Configurable接口,在Configurable接口中可以为一个Tool设置初始化配置,即使用setConf()方法 
 
 
     tool.setConf(conf);
     
      
 // 
 get the args w/o generic hadoop args 
 
 
     String[] toolArgs  
 = 
  parser.getRemainingArgs();  
 // 
  返回从控制台输入的命令行参数的数组 
 
 
      
 return 
  tool.run(toolArgs);  
 // 
  根据toolArgs数组指定的命令启动WordCount实例运行,返回实现Tool接口的实现类的对象的执行状态码 
 
 
 }


上面的run方法应该是执行WordCount例子的最高层的方法,最抽象了。

程序一开始,首先要解析Hadoop配置文件,对应于Hadoop根目录下的conf目录下。其中的配置类为Configuration,构造一个Configuration对象,使用如下所示构造方法:


public 
  Configuration() {
      
 if 
  (LOG.isDebugEnabled()) {
       LOG.debug(StringUtils.stringifyException( 
 new 
  IOException( 
 " 
 config() 
 " 
 )));
     }
     resources.add( 
 " 
 hadoop-default.xml 
 " 
 );
     resources.add( 
 " 
 hadoop-site.xml 
 " 
 );
 }


实例化一个Configuration对象,就是将conf目录中的hadoop-default.xml和hadoop-site.xml配置文件加入到private ArrayList<Object> resources中,以便再进一步解析。

真正解析Hadoop的配置文件的是一个GenericOptionsParser通用选项解析器类,需要提供一个Configuration对象的,同时指定一个命令行参数数组。

如下是GenericOptionsParser类的构造方法:


public 
  GenericOptionsParser(Configuration conf, String[] args) {
      
 this 
 (conf,  
 new 
  Options(), args);  
 // 
  这里额外又多增加了一个Options对象作为参数 
 
 
 }


Options类是一个选项对象的集合,用于描述在应用中可能使用到的命令行参数。可以通过查看Options类的构造方法:


public 
  Options()
     {
          
 // 
  nothing to do 
 
 
     }


其实,什么也没有做。然而,可以动态为一个Options对象添加指定的选项的。

又调用了GenericOptionsParser类的另一个构造方法,如下所示:

public 
  GenericOptionsParser(Configuration conf, Options options, String[] args) {
     parseGeneralOptions(options, conf, args);
 }

继续调用GenericOptionsParser类的成员方法parseGeneralOptions()来进一步解析配置选项:


/** 
 
    * Parse the user-specified options, get the generic options, and modify
    * configuration accordingly
    *  
 @param 
  conf Configuration to be modified
    *  
 @param 
  args User-specified arguments
    *  
 @return 
  Command-specific arguments
     
 */ 
 
 
 private 
  String[] parseGeneralOptions(Options opts, Configuration conf, 
       String[] args) {
     opts  
 = 
  buildGeneralOptions(opts);
     CommandLineParser parser  
 = 
   
 new 
  GnuParser();
      
 try 
  {
       commandLine  
 = 
  parser.parse(opts, args,  
 true 
 );
       processGeneralOptions(conf, commandLine);
        
 return 
  commandLine.getArgs();
     }  
 catch 
 (ParseException e) {
       LOG.warn( 
 " 
 options parsing failed:  
 " 
 + 
 e.getMessage());
       HelpFormatter formatter  
 = 
   
 new 
  HelpFormatter();
       formatter.printHelp( 
 " 
 general options are:  
 " 
 , opts);
     }
      
 return 
  args;
 }


其中,commandLine是GenericOptionsParser类的一个私有成员变量。

上面GenericOptionsParser类的成员方法parseGeneralOptions()可以作为解析Hadoop配置选项的一个高层的抽象方法了。

其中的buildGeneralOptions()接收Options opts然后又返回了opts,如下所示:


/** 
 
    * Specify properties of each generic option
     
 */ 
 
 @SuppressWarnings( 
 " 
 static-access 
 " 
 )
 
 private 
  Options buildGeneralOptions(Options opts) {
     Option fs  
 = 
  OptionBuilder.withArgName( 
 " 
 local|namenode:port 
 " 
 )
     .hasArg()
     .withDescription( 
 " 
 specify a namenode 
 " 
 )
     .create( 
 " 
 fs 
 " 
 );
     Option jt  
 = 
  OptionBuilder.withArgName( 
 " 
 local|jobtracker:port 
 " 
 )
     .hasArg()
     .withDescription( 
 " 
 specify a job tracker 
 " 
 )
     .create( 
 " 
 jt 
 " 
 );
     Option oconf  
 = 
  OptionBuilder.withArgName( 
 " 
 configuration file 
 " 
 )
     .hasArg()
     .withDescription( 
 " 
 specify an application configuration file 
 " 
 )
     .create( 
 " 
 conf 
 " 
 );
     Option property  
 = 
  OptionBuilder.withArgName( 
 " 
 property=value 
 " 
 )
     .hasArgs()
     .withArgPattern( 
 " 
 = 
 " 
 ,  
 1 
 )
     .withDescription( 
 " 
 use value for given property 
 " 
 )
     .create( 
 ' 
 D 
 ' 
 );
     opts.addOption(fs);
     opts.addOption(jt);
     opts.addOption(oconf);
     opts.addOption(property);
     
      
 return 
  opts;
 }


返回的Options opts已经被赋予了丰富的内容