Hadoop源码研究 -- >Configuration

1. 介绍

        Congfiguration类位于hadoop-common工程下的org.apache.hadoop.conf包下,它是Hadoop系统的配置文件类,如core-default.xml, hdfs-site.xml都是有本类进行读取和配置的,它保存了整个Hadoop运行环境的上下文。研究本类对于了解Hadoop读取配置文件的方式,运行时动态改变配置很有帮助!

        特别注意:本文基于Hadoop 2.4版本进行研究

2. 详解

2.1 Configuartion概述

        Configuration类采用懒加载模式(Lazy-Load)加载配置文件,内部由一个Properties对象properties存储所有的配置信息,并由一个Set对象finalParameters存储所有的final属性名,当调用Congfiguration.getXXX()时,会检测properties成员变量是否为空,如果为空则发起加载动作,否则直接获取属性的值。此外Configuration类实现了Iterator接口,也就是说你可以直接通过foreach进行遍历。

        Configuration配置文件中的property标签包含一下几种子标签:

name:属性名
value:属性值
final:是否可覆盖
source:属性的来源(记录属性来自那个Resource,这里存的就是Resource的name属性)
description:属性的描述信息,这个值不在Configuration中存储,仅用于用户阅读,除此之外其他属性都会存储下来
2.1 Configuartion主要方法和内部类

        1. Resource内部类

         Resource内部类代表一个配置资源(如:core-site.xml),有一个资源名称和资源对象(可以是String,URL,Path,InputStream等)构成,Configuration类有两种Resource:一种为默认配置资源(如:core-default.xml,core-site.xml等,由CopyOnWriteArrayList静态对象defaultResources存储),一种为用户自定义配置资源(由有一个ArrayList对象resources存储)

  /**
   * List of default Resources. Resources are loaded in the order of the list 
   * entries
   */
  private static final CopyOnWriteArrayList<String> defaultResources = new CopyOnWriteArrayList<String>();
 
  /**
   * List of configuration resources.
   */
  private ArrayList<Resource> resources = new ArrayList<Resource>();
        2. reloadConfiguration方法

        该方法并不会真正触发加载动作,只是把properties置空,源码如下:

  public synchronized void reloadConfiguration() {
    properties = null;                            // trigger reload
    finalParameters.clear();                      // clear site-limits
  }
        3. addResourceObject

        该方法是添加用户配置资源的私有方法,它是通过一系列addResource重载方法向外提供服务,addResource根据资源对象的类型(String,URL,Path,InputStream等)定义了若干种重载方法,源码如下:

  public void addResource(Configuration conf) {
    addResourceObject(new Resource(conf.getProps()));
  }
  
  private synchronized void addResourceObject(Resource resource) {
    resources.add(resource);                      // add to resources
    reloadConfiguration(); // 每添加一次资源都会置空一次properties
  }
        4. addDefaultResource
        该方法是添加默认配置资源的public static方法,一般在静态代码块调用,代码如下:

  /**
   * Add a default resource. Resources are loaded in the order of the resources 
   * added.
   * @param name file name. File should be present in the classpath.
   */
  public static synchronized void addDefaultResource(String name) {
    if(!defaultResources.contains(name)) {
      defaultResources.add(name);
      for(Configuration conf : REGISTRY.keySet()) {
        if(conf.loadDefaults) {
          conf.reloadConfiguration();
        }
      }
    }
  }
        5. substituteVars私有方法
        该方法是使用系统变量替换某一配置value中的${var}变量,如${user.name}会被替换成系统用户名

        6. getProps方法

        该方法是触发加载的最终方法

  protected synchronized Properties getProps() {
    if (properties == null) {//如果properties为null,则加载配置
      properties = new Properties();
      HashMap<String, String[]> backup = 
        new HashMap<String, String[]>(updatingResource);
      loadResources(properties, resources, quietmode);
      if (overlay!= null) {
        properties.putAll(overlay);
        for (Map.Entry<Object,Object> item: overlay.entrySet()) {
          String key = (String)item.getKey();
          updatingResource.put(key, backup.get(key));
        }
      }
    }
    return properties;
  }
        7. get方法

        该方法是Configuration类中最重要的取值方法,这里

  public String get(String name) {
    String[] names = handleDeprecation(deprecationContext.get(), name);
    String result = null;
    for(String n : names) {
      result = substituteVars(getProps().getProperty(n));//这里调用了getProps,并且用substituteVars替换了变量
    }
    return result;
  }
        8. loadResource和loadResources私有方法

        这两个方法都是私有方法,也就是说Configuration类一定是懒加载的,只有在get属性的时候才会加载配置,loadResource是加载单个Resource,由loadResources调用实现多个Resource加载

        loadResource方法比较复杂,主要实现资源对象类型检测,并最终转化为xml的root元素进行解析(除资源类型为Properties外,它会直接覆盖目标propertis中然后方法推出,所以这里可能将final的值也给覆盖了,不过不用担心,在对外暴露的众多addResource重载方法中,并没有一个是添加Properties这种类型资源的,这个只是Configuration内部将InputStream这种资源类型转换成了Properties资源类型后使用的),

  private Resource loadResource(Properties properties, Resource wrapper, boolean quiet) {
    String name = UNKNOWN_RESOURCE;
    try {
      Object resource = wrapper.getResource();
      name = wrapper.getName();
      
      //构建DocumentBuilder 略
      Document doc = null;
      Element root = null;
      boolean returnCachedProperties = false;//是否转化为Properties资源类型
      
      if (resource instanceof URL) {                  // an URL resource
        doc = parse(builder, (URL)resource);
      } else if (resource instanceof String) {        // a CLASSPATH resource
        URL url = getResource((String)resource);
        doc = parse(builder, url);
      } else if (resource instanceof Path) {          // a file resource
        // Can't use FileSystem API or we get an infinite loop
        // since FileSystem uses Configuration API.  Use java.io.File instead.
        File file = new File(((Path)resource).toUri().getPath())
          .getAbsoluteFile();
        if (file.exists()) {
          if (!quiet) {
            LOG.debug("parsing File " + file);
          }
          doc = parse(builder, new BufferedInputStream(
              new FileInputStream(file)), ((Path)resource).toString());
        }
      } else if (resource instanceof InputStream) {
        doc = parse(builder, (InputStream) resource, null);
        returnCachedProperties = true;//将InputStream类型转化为Properties类型
      } else if (resource instanceof Properties) {
        overlay(properties, (Properties)resource); // 若为Properties类型,则直接覆盖,这里会导致root和doc都为null,后边就会退出
      } else if (resource instanceof Element) {
        root = (Element)resource;
      }
 
      if (root == null) {
        if (doc == null) {
          if (quiet) {
            return null;
          }
          throw new RuntimeException(resource + " not found");
        }
        root = doc.getDocumentElement();
      }
      Properties toAddTo = properties;
      if(returnCachedProperties) {
        toAddTo = new Properties();//看到了吧,转化为Properties了
      }
      if (!"configuration".equals(root.getTagName()))
        LOG.fatal("bad conf file: top-level element not <configuration>");
      NodeList props = root.getChildNodes();
      DeprecationContext deprecations = deprecationContext.get();
      for (int i = 0; i < props.getLength(); i++) {
        Node propNode = props.item(i);
        if (!(propNode instanceof Element))
          continue;
        Element prop = (Element)propNode;
        if ("configuration".equals(prop.getTagName())) {
          loadResource(toAddTo, new Resource(prop, name), quiet);
          continue;
        }
        if (!"property".equals(prop.getTagName()))
          LOG.warn("bad conf file: element not <property>");
        NodeList fields = prop.getChildNodes();
        String attr = null;
        String value = null;
        boolean finalParameter = false;
        LinkedList<String> source = new LinkedList<String>();
        for (int j = 0; j < fields.getLength(); j++) {
          Node fieldNode = fields.item(j);
          if (!(fieldNode instanceof Element))
            continue;
          Element field = (Element)fieldNode;
          if ("name".equals(field.getTagName()) && field.hasChildNodes())
            attr = StringInterner.weakIntern(
                ((Text)field.getFirstChild()).getData().trim());
          if ("value".equals(field.getTagName()) && field.hasChildNodes())
            value = StringInterner.weakIntern(
                ((Text)field.getFirstChild()).getData());
          if ("final".equals(field.getTagName()) && field.hasChildNodes())
            finalParameter = "true".equals(((Text)field.getFirstChild()).getData());
          if ("source".equals(field.getTagName()) && field.hasChildNodes())
            source.add(StringInterner.weakIntern(
                ((Text)field.getFirstChild()).getData()));//把property属性中的source属性加入source中
        }
        source.add(name);//这里又加了一个资源的名字,看函数开始部分
        
        // Ignore this parameter if it has already been marked as 'final'
        if (attr != null) {
          if (deprecations.getDeprecatedKeyMap().containsKey(attr)) {
            DeprecatedKeyInfo keyInfo =
                deprecations.getDeprecatedKeyMap().get(attr);
            keyInfo.clearAccessed();
            for (String key:keyInfo.newKeys) {
              // update new keys with deprecated key's value 
              loadProperty(toAddTo, name, key, value, finalParameter, 
                  source.toArray(new String[source.size()]));
            }
          }
          else {
            loadProperty(toAddTo, name, attr, value, finalParameter, 
                source.toArray(new String[source.size()]));//调用函数加载一个具体的property
          }
        }
      }
      
      if (returnCachedProperties) {
        overlay(properties, toAddTo);
        return new Resource(toAddTo, name);
      }
      return null;
    } catch (IOException e) {
      LOG.fatal("error parsing conf " + name, e);
      throw new RuntimeException(e);
    } catch (DOMException e) {
      LOG.fatal("error parsing conf " + name, e);
      throw new RuntimeException(e);
    } catch (SAXException e) {
      LOG.fatal("error parsing conf " + name, e);
      throw new RuntimeException(e);
    } catch (ParserConfigurationException e) {
      LOG.fatal("error parsing conf " + name , e);
      throw new RuntimeException(e);
    }
  }
        loadResources方法根据资源列表依次加载,并将loadResource返回的Resource发到资源列表的对应位置
  private void loadResources(Properties properties,
                             ArrayList<Resource> resources,
                             boolean quiet) {
    if(loadDefaults) {
      for (String resource : defaultResources) {//加载默认资源
        loadResource(properties, new Resource(resource), quiet);
      }
    
      //support the hadoop-site.xml as a deprecated case
      if(getResource("hadoop-site.xml")!=null) {
        loadResource(properties, new Resource("hadoop-site.xml"), quiet);
      }
    }
    
    for (int i = 0; i < resources.size(); i++) {//加载用户自定义资源
      Resource ret = loadResource(properties, resources.get(i), quiet);
      if (ret != null) {
        resources.set(i, ret);//资源列表被改变了,原来的InputStream资源类型变成了Properties类型
      }
    }
  }
        9. loadProperty私有方法

        用来添加一个property到指定的Properties中,并标记该property的来源到updatingResource中,如果是final的,还会将其属性名加到finalParameters中,源码如下:

  private void loadProperty(Properties properties, String name, String attr,
      String value, boolean finalParameter, String[] source) {
    if (value != null) {
      if (!finalParameters.contains(attr)) {
        properties.setProperty(attr, value);
        updatingResource.put(attr, source);
      } else if (!value.equals(properties.getProperty(attr))) {//如果为final的,则不允许覆盖
        LOG.warn(name+":an attempt to override final parameter: "+attr
            +";  Ignoring.");
      }
    }
    if (finalParameter) {
      finalParameters.add(attr);
    }
  }
        参数说明:

        properties:存放property

        name:属性来源的Resource的name属性

        attr:属性名

        value:属性值

        finalParameter:是否为final

        source:属性的所有来源(包括该属性中配置的source值和属性所在的Resource的name属性)

        10. 其他的get和set方法就不用说了,很简单无非是类型转换而已

2.3 Configuartion家族

        1.位于org.apache.hadoop.conf下的Configuration类加载了默认配置文件core-default.xml和core-site.xml(hadoop-site.xml已经废弃),并没有加载HDFS的配置文件,也就是说,如果你new了Configuration则只能获取core的配置,那么使用HDFS API(如FileSystem)编程时怎么办呢?

         实际上,在Hadoop 2.4中各个组件(Yarn,MR,HDFS等)都继承了Configuration从而加载了自己的配置文件,子类如下:

         HdfsConfiguration:加载hdfs-default.xml和hdfs-site.xml

         YarnConfiguration:加载yarn-default.xml和yarn-site.xml

         JobConf:加载yarn-default.xml,yarn-site.xml,mapred-default.xml和mapred-site.xml

         另外还有FairSchedulerConfiguration和CapacitySchedulerConfiguration

         所以一定要注意了,如果使用HDFS的FileSystem应该像这样:

    Configuration conf = new HdfsConfiguration();//new 一个HdfsConfiguration
    
    FileSystem fs = null;
    try {
      fs = FileSystem.get(URI.create("hdfs://ns1:8009"), conf);
      fs.listFiles(new Path("/"), false);
    } catch (IOException e) {
      e.printStackTrace();
    }
3. 总结

         本文阐述了Configuration的原理和一些主要方法的实现,最后指使用方法。

4. 参考资料

http://hadoop.apache.org/
--------------------- 
作者:zhoubangtao 
来源:CSDN 
原文:https://blog.csdn.net/zhoubangtao/article/details/25977561 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/u011500419/article/details/83343654
今日推荐