转自: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(
this
,
null
);
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架构设计与实现原理》