zoukankan      html  css  js  c++  java
  • Hadoop源码分析之Configuration

    转自:http://www.it165.net/admin/html/201312/2178.html

      • org.apache.hadoop.conf.Configuration类是Hadoop所有功能的基础类,每一种功能执行之前都需要有先得到一个Configuration对象。Hadoop使用了XML文件作为配置文件,来保存运行时的配置信息,然后将配置加载到Configuration对象中,要使用配置信息时直接从Configuration对象中取。

        Hadoop配置文件

        将下载的Hadoop压缩包解压后,再文件夹中有一个conf文件夹,这里面有一些Hadoop启动时使用的配置文件,比如配置Hadoop为伪分布式后的core-site.xml文件为:

        01.<?xml version="1.0"?>
        02.<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
        03. 
        04.<!-- Put site-specific property overrides in this file. -->
        05. 
        06.<configuration>
        07.<property>
        08.<name>fs.default.name</name>
        09.<value>hdfs://localhost:9000</value>
        10.</property>
        11.<property>
        12.<name>hadoop.tmp.dir</name>
        13.<value>/home/gmy/hadoop/tmp</value>
        14.</property>
        15.</configuration>
        如上面的xml代码所示,Hadoop的XML配置文件的根节点是configuration,下一层的节点是property,每个property都代表一个配置项,由键值对组成,name节点表示该配置项的键,value节点表示值,除了name和value节点,property节点另一个重要的子节点是final,它表示这个键值对是不可覆盖的,是固定不变的,和Java中的final关键字类似。property节点还有个description子节点,是对该属性的描述,类似于Java的注释,在程序中不使用。

        property的保存

        在Configuration类中,有个java.util.Properties类型的成员变量properties,它保存了所有读取到的键值对配置项。调用Configuration.get()方法时,是从properties对象中取值,Configuration.get()的相关代码如下:

        01.public String get(String name) {
        02.return substituteVars(getProps().getProperty(name));
        03.}
        04.private synchronized Properties getProps() {
        05.if (properties == null) {
        06.properties = new Properties();
        07.loadResources(properties, resources, quietmode);
        08.if (overlay!= null) {
        09.properties.putAll(overlay);
        10.for (Map.Entry<Object,Object> item: overlay.entrySet()) {
        11.updatingResource.put((String) item.getKey(), UNKNOWN_RESOURCE);
        12.}
        13.}
        14.}
        15.return properties;
        16.}

        其中get()方法中调用的substituteVars()是进行属性扩展,下面会有这个方法的介绍。

        属性扩展

        再get()方法中调用方法substituteVars()是对配置的属性扩展,那么什么是属性扩展呢?举例说明如下,如果配置项dfs.name.dir值是${hadoop.tmp.dir}/dfs/name,而配置项hadoop.tmp.dir的值是/data, 那么${hadoop.tmp.dir}会使用hadoop.tmp.dir的值/data进行扩展,扩展后dfs.name.dir的值为/data/dfs/name。再Configuration类中,方法substituteVars()就是用来进行属性扩展的,代码如下:

        01.//正则表达式对象,包含正则表达式${[^}$ ]+},u0020是unicode中标识空格的十六进制
        02.private static Pattern varPat = Pattern.compile("\$\{[^\}\$u0020]+\}");
        03.//最多做20次扩展
        04.private static int MAX_SUBST = 20;
        05./**
        06.* 进行属性扩展,可以扩展保存再Configuration对象中的键值对,而且还可以使用Java虚拟机的系统属性,
        07.* 在该方法中属性扩展优先使用系统属性
        08.* @param expr
        09.* @return
        10.*/
        11.private String substituteVars(String expr) {
        12.if (expr == null) {
        13.return null;
        14.}
        15.Matcher match = varPat.matcher("");
        16.String eval = expr;
        17.//循环,做多做MAX_SUBST次属性扩展
        18.for(int s=0; s<MAX_SUBST; s++) {
        19.match.reset(eval);
        20.if (!match.find()) {
        21.return eval;//什么都没有找到,返回
        22.}
        23.String var = match.group();
        24.//获得属性扩展的键
        25.var = var.substring(2, var.length()-1);
        26.String val = null;
        27.try {
        28.//先查看系统属性中是否有var对应的值,保证优先使用系统属性
        29.val = System.getProperty(var);
        30.catch(SecurityException se) {
        31.LOG.warn("Unexpected SecurityException in Configuration", se);
        32.}
        33.//如果系统属性中没有,则查看Configuration保存再键值对中是否有var的键值对
        34.if (val == null) {
        35.val = getRaw(var);
        36.}
        37.if (val == null) {
        38.//没有找到的,则返回
        39.return eval; // return literal ${var}: var is unbound
        40.}
        41.// 进行替换
        42.eval = eval.substring(0, match.start())+val+eval.substring(match.end());
        43.}
        44. 
        45.throw new IllegalStateException("Variable substitution depth too large: "
        46.+ MAX_SUBST + " " + expr);
        47.}
        在Configuration类中,使用正则表达式${[^}$ ]+}来匹配表达式expr中的${}符号,正则表达式中的${是匹配expr中的前面部分${,}匹配expr中的后面部分},[^}$ ]表示匹配除了^、}和$这三个字符的所有字符,而表达式中的+表示其前面的[^}$ ]至少出现一次。在substituteVars()方法中先得到一个Matcher对象,然后循环MAX_SUBST次对expr中匹配正则表达式的属性进行值替换(如果存在)。可以看到优先匹配系统属性,其次是Configuration.properties中的键值对。

        延迟加载

        Configuration类采取了一种延迟加载的方式来加载XML配置文件中的键值对。使用语句

        1.Configuration conf = new Configuration();
        按照Java初始化顺序,先初始化静态域(static 变量和代码块),再初始化非静态成员变量,最后执行构造方法,那么新建一个Configuration对象conf时,先执行Configuration类中的静态域(static 变量和代码块),再初始化非静态成员变量,最后执行Configuration()这个构造方法,所以新建Configuration对象涉及的代码如下:
        01./**用来设置加载配置的模式,如果quitemode为true,则再加载解析配置文件的过程中,不输出日志信息,该变量只是一个方便开发人员调试的变量**/
        02.private boolean quietmode = true;
        03. 
        04./**
        05.* List of configuration resources.<br/>
        06.* 保存了所有通过addRescource()方法添加Configuration对象的资源
        07.*/
        08.private ArrayList<Object> resources = new ArrayList<Object>();
        09. 
        10./**
        11.* List of configuration parameters marked <b>final</b>.<br/>
        12.* 用于保存再配置文件中已经被声明为final的键值对的键
        13.*/
        14.private Set<String> finalParameters = new HashSet<String>();
        15./**是否加载默认资源,这些默认资源保存在defaultResources中**/
        16.private boolean loadDefaults = true;
        17. 
        18./**
        19.* Configuration objects<br/>
        20.* 记录了系统中所有的Configuration对象,
        21.*/
        22.private static final WeakHashMap<Configuration,Object> REGISTRY =
        23.new WeakHashMap<Configuration,Object>();
        24. 
        25./**
        26.* List of default Resources. Resources are loaded in the order of the list
        27.* entries<br/>
        28.* 默认资源,通过方法addDefaultResource()可以添加系统默认资源
        29.*/
        30.private static final CopyOnWriteArrayList<String> defaultResources =
        31.new CopyOnWriteArrayList<String>();
        32. 
        33./**
        34.* The value reported as the setting resource when a key is set
        35.* by code rather than a file resource.
        36.*/
        37.static final String UNKNOWN_RESOURCE = "Unknown";
        38. 
        39./**
        40.* Stores the mapping of key to the resource which modifies or loads
        41.* the key most recently
        42.*/
        43.private HashMap<String, String> updatingResource;
        44. 
        45.static{
        46.//print deprecation warning if hadoop-site.xml is found in classpath
        47.ClassLoader cL = Thread.currentThread().getContextClassLoader();
        48.if (cL == null) {
        49.cL = Configuration.class.getClassLoader();
        50.}
        51.if(cL.getResource("hadoop-site.xml")!=null) {
        52.LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
        53."Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
        54."mapred-site.xml and hdfs-site.xml to override properties of " +
        55."core-default.xml, mapred-default.xml and hdfs-default.xml " +
        56."respectively");
        57.}
        58.addDefaultResource("core-default.xml");
        59.addDefaultResource("core-site.xml");
        60.}
        61./**配置文件解析后的键值对**/
        62.private Properties properties;
        63./**用于记录通过set()方式改变的配置项,而不是通过加载配置资源解析得到的变量**/
        64.private Properties overlay;
        65.private ClassLoader classLoader;
        66.{
        67.classLoader = Thread.currentThread().getContextClassLoader();
        68.if (classLoader == null) {
        69.classLoader = Configuration.class.getClassLoader();
        70.}
        71.}
        72. 
        73./** A new configuration. */
        74.public Configuration() {
        75.this(true);
        76.}
        77. 
        78./** A new configuration where the behavior of reading from the default
        79.* resources can be turned off.
        80.*
        81.* If the parameter {@code loadDefaults} is false, the new instance
        82.* will not load resources from the default files.
        83.* @param loadDefaults specifies whether to load from the default files
        84.*/
        85.public Configuration(boolean loadDefaults) {
        86.this.loadDefaults = loadDefaults;
        87.updatingResource = new HashMap<String, String>();
        88.synchronized(Configuration.class) {
        89.REGISTRY.put(thisnull);
        90.}
        91.}
        92.//正则表达式对象,包含正则表达式${[^}$ ]+},u0020是unicode中标识空格的十六进制
        93.private static Pattern varPat = Pattern.compile("\$\{[^\}\$u0020]+\}");
        94.//最多做20次扩展
        95.private static int MAX_SUBST = 20;

        从上面的代码可以看出再新建Configuration对象时并没有读取XML文件中的属性(Property),而只是通过静态方法addDefaultResource()将core-default.xml和core-site.xml这两个XML文件名加入到Configuration.defaultResources静态成员变量中。这样就完成了一个Configuration对象的初始化。再这个过程中并没有从XML文件中读取Property属性的键值对,所以延迟到了需要使用的时候加载。

        加载键值对

        上面说过Configuration采用了延迟加载的方式来加载XML配置文件,那么在什么时候取读取XML文件将XML文件中的键值对加载到内存呢?在调用Configuration.get()方法的时候。上面的property的保存部分说道了使用Configuration.get()方法再properties中通过给定的键取其对应的值,在get()方法中调用了getProps()方法,getProps()方法先判断properties是否为空,如果为空,则调用loadResources()方法来加载XML文件中的键值对保存在properties成员变量中,loadResources()方法的代码如下:

        01.private void loadResources(Properties properties,
        02.ArrayList resources,
        03.boolean quiet) {
        04.if(loadDefaults) {
        05.for (String resource : defaultResources) {
        06.loadResource(properties, resource, quiet);
        07.}
        08. 
        09.//support the hadoop-site.xml as a deprecated case
        10.if(getResource("hadoop-site.xml")!=null) {
        11.loadResource(properties, "hadoop-site.xml", quiet);
        12.}
        13.}
        14. 
        15.for (Object resource : resources) {
        16.loadResource(properties, resource, quiet);
        17.}
        18.}
        loadResources先加载默认的资源(defaultResources中保存),再加载形参resources对应的资源。其中defaultResources表示通过方法addDefaultResource()可以添加系统默认资源,成员变量resources表示所有通过addRescource()方法添加Configuration对象的资源。在loadResource()方法中读取XML文件进行加载。loadResource()方法使用了DOM方式处理XML,逻辑比较简单,具体关于DOM加载XML的方式可以查阅其他资料。

        总结

        Configuration类在Hadoop的Common包中,它是所有Hadoop功能的基石,所以了解Hadoop首先应该知道Configuration类的作用与构造。上面介绍了Configuration的一些主要的变量与方法,可以为后面的其他源码分析打下坚实的基础。

        Reference

        《Hadoop技术内幕:深入解析Hadoop Common和HDFS架构设计与实现原理》

  • 相关阅读:
    2020Java面试题及答案,刷这些题,准没错!
    作为一个面试官,我想问问你Redis分布式锁怎么搞?
    你说研究过Spring里面的源码,循环依赖你会么?
    一口气说出 6种 延时队列的实现方案,面试稳稳的
    我可真是醉了,一个SpringBoot居然问了我30个问题
    最强Dubbo面试题,附带超级详细答案
    平安银行Java社招五面面经,目前最全面的,38个面试题以及答案
    Java电子书高清PDF集合免费下载
    Python处理json模块的详细介绍
    用Python写一个“离线语音提示器”来提醒我们别忘记了时间
  • 原文地址:https://www.cnblogs.com/cxzdy/p/5034778.html
Copyright © 2011-2022 走看看