hadoop实现原理(一)配置信息处理

Hadoop作为一个复杂的软件系统,使用一个配置模块提高其适应性或扩展性,作为其扩展、定制的手段和方式。
为什么不直接使用java.util.Properties类?
Properties类继承自Hashtable,它并不能支持INI文件的“节”,对配置项进行分类。Properties类提供了load()方法加载,该方法从输入流中读入key-value对,而store()方法则将Properties表中的属性列表写入输出流。还提供了loadFromXML()和storeToXML()方法。相应get和set方法:
public String getProperty(String key)
public String getProperty(String key, String defaultValue)
public synchronized Object setProperty(String key, String value)
由于java.util.Properties提供的能力有限,Java社区出现了大量的配置信息读写方案,其中比较著名的是Apache Jakarta Commons工具集中提供的Commons Configuration。
Hadoop没有使用Properties类,也没有使用Commons Configuration来管理配置文件,而使用了一套独有的配置文件管理系统,并提供自己的API,即使用org.apache.hadoop.conf.Configuration。
Hadoop配置文件的格式

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
  <name>hadoop.tmp.dir</name>
  <value>/tmp/hadoop-${user.name}</value>
</property>
<property>
  <name>fs.default.name</name>
  <value>hdfs://localhost:54310</value>
</property>
<property>
  <name>mapred.job.tracker</name>
  <value>hdfs://localhost:54311</value>
</property>
<property> 
  <name>dfs.replication</name>
  <value>8</value>
</property>
<property>
  <name>mapred.child.java.opts</name>
  <value>-Xmx512m</value>
</property>
</configuration>

Hadoop配置文件的根元素是configuration,一般只包含子元素property。Hadoop可以合并资源将多个配置文件合并,产生一个配置。如下:
Configurationconf = new Configuration();
conf.addResource(“core-default.xml”);
conf.addResource(“core-site.xml”)
如果这两个资源都包含了相同的配置项,而且前一个资源的配置项没有标记为final,那么,后面的配置将覆盖前面的配置。如果第一个资源中某配置项标记了final,那么,在加载第二个资源的时候,会有警告提示。
Hadoop配置系统还有一个重要的功能,就是属性扩展。如配置项项dfs.name.dir的值是 Hadoop.tmp.dir/dfs/name {Hadoop.tmp.dir}会使用Configuration中的相应属性值进行扩展。
Configuration的成员变量
这里写图片描述
使用Configuration类的一般过程是:构造Configuration对象,并通过类的addResource()方法添加需要加载的资源:然后就可以用get*方法和set*方法访问和设置配置项。

quitemode,用来设置加载配置的模式,quite=true则不输出日志信息,方便调试。
Resources保存了所有通过addResource()方法添加Configuration对象的资源。Configuration.addResource()有如下4种形式:

public void addResource(InputStream in)
public void addResource(Path file)
public void addResource(String name)
public void addResource(URL url)

在HDFS的DataNode中,加载两个默认资源:
// 代码来自org.apache.hadoop.hdfs.server.datanode.DataNode
static{
Configuration.addDefaultResource(“hdfs-default.xml”);
Configuration.addDefaultResource(“hdfs-site.xml”)
}
properties和overlay成员变量都是前面介绍过的java.util.Properties,Hadoop配置文件解析后的key-value对都存在properties中,变量finalParameters的类型是Set,用来保存所有在配置文件中已经声明为fianl的键,变量overlay用于记录通过set()方式改变的配置项,也就是应用设置的。
资源加载
资源通过对象的addResource()方法或类的静态addDefaultResource()方法(设置了loadDefault标志)添加到Configuration对象中,添加的资源并不会立即被加载,只是通过reloadConfiguration()方法清空properties和finalParameters。相关代码如下:

public void addResource(String name){
    addResourceObject(name);
}
private synchronized void addResourceObject(Object resource) {
    Resources.add(resource);
    reloadConfiguration();
}
public synchronized void reloadConfiguration(){
    Properties = null;
    finalParameters.clear();
}

静态方法addDefaultResource()也能清空Configuration对象中的数据,这是通过类的静态成员REGISTRY作为媒介进行的。

  public static synchronized void addDefaultResource(String name) {
    if(!defaultResources.contains(name)) {
      defaultResources.add(name);
      for(Configuration conf : REGISTRY.keySet()) {
        if(conf.loadDefaults) {
          conf.reloadConfiguration();
        }
      }
    }
  }

成员变量properties中的数据,直到需要的时候才会加载进来。在getProps()方法中,如果发现properties为空,将触发loadResources()方法加载配置资源。延迟加载的设计模式,当真需要配置数据的时候,才开始解析配置文件。

  private synchronized Properties getProps() {
    if (properties == null) {
      properties = new Properties();
      loadResources(properties, resources, quietmode);
      if (overlay!= null) {
        properties.putAll(overlay);
        if (storeResource) {
          for (Map.Entry<Object,Object> item: overlay.entrySet()) {
            updatingResource.put((String) item.getKey(), "Unknown");
          }
        }
      }
    }
    return properties;
  }

由于Hadoop的配置文件都是很小的文件,因此Configuration使用DOM处理XML。

 private void loadResources(Properties properties,
                             ArrayList resources,
                             boolean quiet) {
    if(loadDefaults) {
      for (String resource : defaultResources) {
        loadResource(properties, resource, quiet);
      }

      //support the hadoop-site.xml as a deprecated case
      if(getResource("hadoop-site.xml")!=null) {
        loadResource(properties, "hadoop-site.xml", quiet);
      }
    }

    for (Object resource : resources) {
      loadResource(properties, resource, quiet);
    }
  }
  private void loadResource(Properties properties, Object name, boolean quiet) {
    try {
      DocumentBuilderFactory docBuilderFactory 
        = DocumentBuilderFactory.newInstance();
      //ignore all comments inside the xml file
      docBuilderFactory.setIgnoringComments(true);

      //allow includes in the xml file
      docBuilderFactory.setNamespaceAware(true);
      try {
          docBuilderFactory.setXIncludeAware(true);
      } catch (UnsupportedOperationException e) {
        LOG.error("Failed to set setXIncludeAware(true) for parser "
                + docBuilderFactory
                + ":" + e,
                e);
      }
      DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
      Document doc = null;
      Element root = null;

      if (name instanceof URL) {                  // an URL resource
        URL url = (URL)name;
        if (url != null) {
          if (!quiet) {
            LOG.info("parsing " + url);
          }
          doc = builder.parse(url.toString());
        }
      } else if (name instanceof String) {        // a CLASSPATH resource
        URL url = getResource((String)name);
        if (url != null) {
          if (!quiet) {
            LOG.info("parsing " + url);
          }
          doc = builder.parse(url.toString());
        }
      } else if (name 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)name).toUri().getPath())
          .getAbsoluteFile();
        if (file.exists()) {
          if (!quiet) {
            LOG.info("parsing " + file);
          }
          InputStream in = new BufferedInputStream(new FileInputStream(file));
          try {
            doc = builder.parse(in);
          } finally {
            in.close();
          }
        }
      } else if (name instanceof InputStream) {
        try {
          doc = builder.parse((InputStream)name);
        } finally {
          ((InputStream)name).close();
        }
      } else if (name instanceof Element) {
        root = (Element)name;
      }

      if (doc == null && root == null) {
        if (quiet)
          return;
        throw new RuntimeException(name + " not found");
      }

      if (root == null) {
        root = doc.getDocumentElement();
      }
      if (!"configuration".equals(root.getTagName()))
        LOG.fatal("bad conf file: top-level element not <configuration>");
      NodeList props = root.getChildNodes();
      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(properties, prop, 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;
        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 = ((Text)field.getFirstChild()).getData().trim();
          if ("value".equals(field.getTagName()) && field.hasChildNodes())
            value = ((Text)field.getFirstChild()).getData();
          if ("final".equals(field.getTagName()) && field.hasChildNodes())
            finalParameter = "true".equals(((Text)field.getFirstChild()).getData());
        }

        // Ignore this parameter if it has already been marked as 'final'
        if (attr != null) {
          if (value != null) {
            if (!finalParameters.contains(attr)) {
              properties.setProperty(attr, value);
              if (storeResource) {
                updatingResource.put(attr, name.toString());
              }
            } else if (!value.equals(properties.getProperty(attr))) {
              LOG.warn(name+":a attempt to override final parameter: "+attr
                     +";  Ignoring.");
            }
          }
          if (finalParameter) {
            finalParameters.add(attr);
          }
        }
      }

    } catch (IOException e) {
      LOG.fatal("error parsing conf file: " + e);
      throw new RuntimeException(e);
    } catch (DOMException e) {
      LOG.fatal("error parsing conf file: " + e);
      throw new RuntimeException(e);
    } catch (SAXException e) {
      LOG.fatal("error parsing conf file: " + e);
      throw new RuntimeException(e);
    } catch (ParserConfigurationException e) {
      LOG.fatal("error parsing conf file: " + e);
      throw new RuntimeException(e);
    }
  }

使用get*和set*访问和设置配置项
get方法在Configuration对象中获取相应的配置信息。

public String get(String name, String defaultValue)

Configuration.get()会调用Conguration的私有方法substituteVars(),完成配置属性的扩展,也就是把包含${key}这种格式的变量,自动替换成对应的值。substituteVars的工作依赖于正则表达式:
varPat: $\\{[^\\}\$\u0020]+\}
如果一次属性扩展完成以后,得到的表达式里仍然包含可扩展的变量,那么,substituteVars()需要再次进行属性扩展。为了避免进入死循环,substituteVars()使用一个非常简单而有效的策略,即属性扩展只能进行一次的次数(20次,通过Configuration的静态成员变量MAX_SUBST定义)。

  private String substituteVars(String expr) {
    if (expr == null) {
      return null;
    }
    Matcher match = varPat.matcher("");
    String eval = expr;
    for(int s=0; s<MAX_SUBST; s++) {
      match.reset(eval);
      if (!match.find()) {
        return eval;
      }
      String var = match.group();
      var = var.substring(2, var.length()-1); // remove ${ .. }
      String val = null;
      try {
        val = System.getProperty(var);
      } catch(SecurityException se) {
        LOG.warn("Unexpected SecurityException in Configuration", se);
      }
      if (val == null) {
        val = getRaw(var);
      }
      if (val == null) {
        return eval; // return literal ${var}: var is unbound
      }
      // substitute
      eval = eval.substring(0, match.start())+val+eval.substring(match.end());
    }
    throw new IllegalStateException("Variable substitution depth too large: " 
                                    + MAX_SUBST + " " + expr);
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值