Hadoop深入研究之Configuration

Hadoop没有使用java.util.Properties管理配置文件,也没有用Apache Jakarta Commons Configuration管理配置文件,而是使用了一套独有的配置文件管理系统,使用org.apache.hadoop.conf.Configuration处理配置信息。


Hadoop的配置文件采用xml格式

如例子所示:

Hadoop 配置文件的根元素是configuration,一般只包含子元素property

每一个property元素是一个配置项

每个配置项一般包括namevalue和一个关于配置项的描述description,元素final表示这个配置项是固定不变的,在合并资源的时候,可以防止配置项的值被覆盖

Configuration中每个属性都是String类型的,但是值类型可能是多种类型(java中的基本类型、StringFileString数组等)

以上面的为例,getInt(“io.sort.factor”)返回整数10,而getStrings(“dfs.web.ugi”)则会返回一个字符串数组,该数组有两个元素分别是webuserwebgroup

合并资源指将多个配置文件合并,产生一个配置,通过Configuration类的loadResources()方法,把他们合并成一个配置:

如果两个配置自愿都包含了相同的配置项,而且前一个自愿的配置项没有被标记为final,后面的配置就会覆盖前面的配置,core-site.xml中的配置将覆盖core-default.xml中的同名配置。如果在第一个资源core-default.xml中某配置项被标记为final,那么,在加载第二个资源的时候,会有警告提示。


Hadoop配置系统还有一个很重要的功能,就是属性扩展。如配置项dfs.name.dir的值是${hadoop.tmp.dir}/dfs/name,其中,${hadoop.tmp.dir}会使用Configuration中的相应属性值进行扩展。如果hadoop.tmp.dir的值是“/data”,那么扩展后的dfs.name.dir的值就是“/data/dfs/name”

使用Configuration类的一般过程:

构造Configuration对象,并通过类的addResource()方法添加需要加载的资源;然后就可以使用get*方法和set*方法访问/设置配置项,资源会在第一次使用的时候自动加载到对象中。



Configuration7个主要的非静态成员变量

quietmode:设置加载配置的模式,默认值是true,表示在加载解析配置文件的过程中,不输出日志信息。

resources保存了所有通过addResource()方法添加Configuration对象的资源

finalParameters保存所有配置文件中已经被声明为final的键--值对的键

loadDefaults:用于是否确定加载默认资源,而这些默认资源保存在defaultResources

defaultResources:静态成员变量,通过addDefaultResources()可以添加系统的默认资源。

我们所常见的core-default.xml、core-site.xml、hdfs-default.xmlhdfs-site.xml、mapred-default.xmlmapred-site.xml等都属于默认资源。

REGISTRYWeakHashMap<ConfigurationObject>类型,用于多个对象的相关配置的注册及对它们进行管理,记录了所有的Configuration

propertieshadoop配置文件解析后的键值对,存放在此成员变量中

资源配置文件中的属性会加载到Properties属性中来

overlay:记录通过set()方式改变的配置项。出现在overlay中的键值对是应用设置的,而不是通过对配置资源解析得到的。

classLoader:类加载器变量,可以用来加载指定类或者加载相关的资源

addResource()可以通过字符串方式加载CLASSPATH资源,它其实通过Configuration中的getResource()将字符串转换成URL资源,相关代码如下:

=====================================================================================================

静态代码块:

以上代码会在调用构造方法之前执行,会加载core-default.xmlcore-site.xml两个文件,Configuration的子类也会同样加载这两个文件。


构造方法:

public Configuration() 

 public Configuration(boolean loadDefaults

public Configuration(Configuration other)


第三个构造函数是克隆一个Configuration对象,调用的是object类的clone方法

1行代码处,我们看见将新创建的这个Configuration放到REGISTRY变量中


资源加载:

资源通过对象的addResource方法或类的静态addDefaultResources()方法(设置了loadDefaults标志)添加到Configuration对象中相关代码如下:

静态成员REGISTRY记录了系统中所有的Configuration对象,所以addDefaultResource()被调用时。

add之后会调用reloadConfiguration()方法清空propertiesfinalParameters

遍历REGISTRY中的元素并在元素(Configuration对象)上调用reloadConfiguration(),即可触发资源的重新加载。

 

重新加载Configuration就是重新将里面的属性记录清空这就将内存中存在的name-value都清空了

这里我们看到会清空propertriesfinalParameters


Get*和Set*访问/设置配置项

文章开头大致的介绍了get*方法,这些方法里最重要的是get()方法,它根据配置项的键获取对应的值,如果键不存在,则返回默认值defaultValue

其他的方法都会依赖于Configuration.get(),并在get()的基础上做进一步处理。get()方法如下:

 


首先将该属性的name的值,这个属性有可能是数组,将其值或者被替代的值放到数组中,然后观察getProps()方法。

代码1行处,我们发现添加的资源并不会立即被加载,但也就是刚才add之后资源并不会立即被加载,说明hadoopConfiguration被设计成了懒加载,即在需要时才会被加载。当真正需要配置数据的时候,才开始分析配置文件。 

成员变量properties中的数据,直到需要的时候才会加载进来。在getProps()方法中,如果发现properties为空,将触发loadResources()方法加载配置资源。 

我们再次进入loadResources(propertiesresourcesquietmode);

既然是分析配置文件,那么在进入该方法之前,我们先来看看配置文件的格式

如文章开头的例子,Hadoop的配置文件都是XML形式,JAXPJava API for XML Processing)是一种稳定、可靠的XML处理API,支持SAXSimple API for XML)和DOMDocument Object Model)两种XML处理方法。

SAX提供了一种流式的、事件驱动的XML处理方式,但编写处理逻辑比较复杂,比较适合处理大的XML文件。

DOMSAX不同,其工作方式是:首先将XML文档一次性装入内存;然后根据文档中定义的元素和属性在内存中创建一个树形结构,也就是一个文档对象模型,将文档对象化,文档中每个节点对应着模型中一个对象;然后使用对象提供的编程接口,访问XML文档进而操作XML文档。由于Hadoop的配置文件都是很小的文件,因此Configuration使用DOM处理XML

接下来我们查看该方法。

这里1处的代码会调用到这里


属性扩展

这里就完成了加载,然后我们回到初始地方

substituteVars(getProps().getProperty(n));

来看看substituteVars()函数的原型

这个我 们看到便是属性扩展,这个是配合正则表达式对象对含有环境变量的参数值进行解析的方法


set*方法

Set类似get,只不过相比get来说比较简单

相对于get*来说,set*的大多数方法都很简单,这些方法对输入进行类型转换等处理后,最终都调用了下面的Configuration.set()方法:

public String set(String name, String value) 

对比相对复杂的Configuration.get(),成员函数set()只是简单地调用了成员变量properties和overlay的setProperty()方法,保存传入的键–值对。



总结:

   1、Configuration的构造函数里面默认loadDefaults是true,即表示加载默认配置资源 

      加载资源有两种方法:

addDefaultResource()

addResource()

      前者是加载默认资源,静态方法,后者是加载指定的资源

      加载的时候是懒加载,即不会立即加载资源,用到的时候才会加载资源

      但是都会调用到reloadConfiguration是重新加载,这里只是清除原来的数据

    2、所有的get*方法都会调用到get方法,

       而get方法又会调用getProps()方法,触发加载资源,完成后进入

       substituteVars方法,进行属性扩展


参考资料:

http://www.2cto.com/kf/201412/357011.html

http://www.hackbase.com/tech/2014-12-12/70073.html

Hadoop技术内幕:深入解析Hadoop Common和HDFS架构设计与实现原理(蔡斌 陈湘萍)

================================================================================================

附:


摘自 H adoop技术内幕,个人感觉作者分析的很透彻,特意贴上来

属性扩展:

varPat:\$\{[^\}\$ ]+\} 

由于“$”、左花括号“{”、右花括号“}”都是正则表达式中的保留字,因此需要通过“\”进行转义。正则表达式varPat中,“\$\{”部分用于匹配${key}中的key前面的“${”,最后的“\}”部分匹配属性扩展项的右花括号“}”,中间部分“[^\}\$ ]+”用于匹配属性扩展键,它使用了两个正则表达式规则:

[^ ]规则,通过[^ ]包含一系列的字符,使表达式匹配这一系列字符以外的任意一个字符。也就是说,“[^\}\$ ]”将匹配除了“}”、“$”和空格以外的所有字符。注意,$后面还包含了一个空格,这个看不见的空格,是通过空格的Unicode字符\u0020添加到表达式中的。

+是一个修饰匹配次数的特殊符号,通过该符号保证了“+”前面的表达式“[^\}\$ ]”至少出现1次。

通过正则表达式“\$\{[^\}\$ ]+\}”,可以在输入字符串里找出需要进行属性扩展的地方,并通过字符串替换,进行属性扩展。

前面提过,如果一次属性扩展完成以后,得到的表达式里仍然包含可扩展的变量,那么,substituteVars()需要再次进行属性扩展。考虑下面的情况:

属性扩展${key1}的结果包含属性扩展${key2},而对${key2}进行属性扩展后,产生了一个包含${key1}的新结果,这会导致属性扩展进入死循环,没办法停止。

针对这种可能发生的情况,substituteVars()中使用了一个非常简单而又有效的策略,即属性扩展只能进行一定的次数(20次,通过Configuration的静态成员变量MAX_SUBST定义),避免出现上面分析的属性扩展死循环。

最后一点需要注意的是,substituteVars()中进行的属性扩展,不但可以使用保存在Configuration对象中的键–值对,而且还可以使用Java虚拟机的系统属性。如系统属性user.home包含了当前用户的主目录,如果用户有一个配置项需要使用这个信息,可以通过属性扩展${user.home},来获得对应的系统属性值。而且,Java命令行可以通过“-D<name>=<value>”的方式定义系统属性。这就提供了一个通过命令行,覆盖或者设置Hadoop运行时配置信息的方法。在substituteVars()中,属性扩展优先使用系统属性,然后才是Configuration对象中保存的键–值对。



DOM加载原理

 

一般的JAXP处理都是从工厂开始,通过调用DocumentBuilderFactory的newInstance()方法,获得用于创建DOM解析器的工厂。这里并没有创建出DOM解析器,只是获得一个用于创建DOM解析器的工厂,接下来需要对上述newInstance()方法得到的docBuilderFactory对象进行一些设置,才能进一步通过DocumentBuilderFactory,得到DOM解析器对象builder。

针对DocumentBuilderFactory对象进行的主要设置包括:

忽略XML文档中的注释;

支持XML空间;

支持XML的包含机制(XInclude)。

XInclude机制允许将XML文档分解为多个可管理的块,然后将一个或多个较小的文档组装成一个大型文档。也就是说,Hadoop的一个配置文件中,可以利用XInclude机制将其他配置文件包含进来一并处理,下面是一个例子:

<configuration xmlns:xi="http://www.w3.org/2001/XInclude">

  ……  

  <xi:include href="conf4performance.xml"/>

  ……  

</configuration> 

通过XInclude机制,把配置文件conf4performance.xml嵌入到当前配置文件,这种方法更有利于对配置文件进行模块化管理,同时就不需要再使用Configuration.addResource()方法加载资源conf4performance.xml了。

设置完DocumentBuilderFactory对象以后,通过docBuilderFactory.newDocumentBuilder()获得了DocumentBuilder对象,用于从各种输入源解析XML。在loadResource()中,需要根据Configuration支持的4种资源分别进行处理,不过这4种情况最终都调用DocumentBuilder.parse()函数,返回一个DOM解析结果。

如果输入是一个DOM的子元素,那么将解析结果设置为输入元素。这是为了处理下面出现的元素configuration包含configuration子节点的特殊情况。

成员函数loadResource的第二部分代码,就是根据DOM的解析结果设置Configuration的成员变量properties和finalParameters。

在确认XML的根节点是configuration以后,获取根节点的所有子节点并对所有子节点进行处理。这里需要注意,元素configuration的子节点可以是configuration,也可以是properties。如果是configuration,则递归调用loadResource(),在loadResource()的处理过程中,子节点会被作为根节点得到继续的处理。

如果是property子节点,那么试图获取property的子元素name、value和final。在成功获得name和value的值后,根据情况设置对象的成员变量properties和finalParameters。









你可能感兴趣的:(Hadoop深入研究之Configuration)