org.apache.hadoop.conf.Configuration类是Hadoop所有功能的基础类,每一种功能执行之前都需要有先得到一个Configuration对象。Hadoop使用了XML文件作为配置文件,来保存运行时的配置信息,然后将配置加载到Configuration对象中,要使用配置信息时直接从Configuration对象中取。
Hadoop配置文件
将下载的Hadoop压缩包解压后,在文件夹中有一个conf文件夹,这里面有一些Hadoop启动时使用的配置文件,比如配置Hadoop为伪分布式后的core-site.xml文件为:
- <?xml version="1.0"?>
- <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
- <!-- Put site-specific property overrides in this file. -->
- <configuration>
- <property>
- <name>fs.default.name</name>
- <value>hdfs://localhost:9000</value>
- </property>
- <property>
- <name>hadoop.tmp.dir</name>
- <value>/home/gmy/hadoop/tmp</value>
- </property>
- </configuration>
property的保存
在Configuration类中,有个java.util.Properties类型的成员变量properties,它保存了所有读取到的键值对配置项。调用Configuration.get()方法时,是从properties对象中取值,Configuration.get()的相关代码如下:
- public String get(String name) {
- return substituteVars(getProps().getProperty(name));
- }
- private synchronized Properties getProps() {
- if (properties == null) {
- properties = new Properties();
- loadResources(properties, resources, quietmode);
- if (overlay!= null) {
- properties.putAll(overlay);
- for (Map.Entry<Object,Object> item: overlay.entrySet()) {
- updatingResource.put((String) item.getKey(), UNKNOWN_RESOURCE);
- }
- }
- }
- return properties;
- }
其中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()就是用来进行属性扩展的,代码如下:
- //正则表达式对象,包含正则表达式\$\{[^\}\$\ ]+\},u0020是unicode中标识空格的十六进制
- private static Pattern varPat = Pattern.compile("\\$\\{[^\\}\\$\u0020]+\\}");
- //最多做20次扩展
- private static int MAX_SUBST = 20;
- /**
- * 进行属性扩展,可以扩展保存在Configuration对象中的键值对,而且还可以使用Java虚拟机的系统属性,
- * 在该方法中属性扩展优先使用系统属性
- * @param expr
- * @return
- */
- private String substituteVars(String expr) {
- if (expr == null) {
- return null;
- }
- Matcher match = varPat.matcher("");
- String eval = expr;
- //循环,做多做MAX_SUBST次属性扩展
- 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);
- String val = null;
- try {
- //先查看系统属性中是否有var对应的值,保证优先使用系统属性
- val = System.getProperty(var);
- } catch(SecurityException se) {
- LOG.warn("Unexpected SecurityException in Configuration", se);
- }
- //如果系统属性中没有,则查看Configuration保存再键值对中是否有var的键值对
- if (val == null) {
- val = getRaw(var);
- }
- if (val == null) {
- //没有找到的,则返回
- return eval; // return literal ${var}: var is unbound
- }
- // 进行替换
- eval = eval.substring(0, match.start())+val+eval.substring(match.end());
- }
- throw new IllegalStateException("Variable substitution depth too large: "
- + MAX_SUBST + " " + expr);
- }
延迟加载
Configuration类采取了一种延迟加载的方式来加载XML配置文件中的键值对。使用语句
- Configuration conf = new Configuration();
- /**用来设置加载配置的模式,如果quitemode为true,则再加载解析配置文件的过程中,不输出日志信息,该变量只是一个方便开发人员调试的变量**/
- private boolean quietmode = true;
- /**
- * List of configuration resources.<br/>
- * 保存了所有通过addRescource()方法添加Configuration对象的资源
- */
- private ArrayList<Object> resources = new ArrayList<Object>();
- /**
- * List of configuration parameters marked <b>final</b>.<br/>
- * 用于保存再配置文件中已经被声明为final的键值对的键
- */
- private Set<String> finalParameters = new HashSet<String>();
- /**是否加载默认资源,这些默认资源保存在defaultResources中**/
- private boolean loadDefaults = true;
- /**
- * Configuration objects<br/>
- * 记录了系统中所有的Configuration对象,
- */
- private static final WeakHashMap<Configuration,Object> REGISTRY =
- new WeakHashMap<Configuration,Object>();
- /**
- * List of default Resources. Resources are loaded in the order of the list
- * entries<br/>
- * 默认资源,通过方法addDefaultResource()可以添加系统默认资源
- */
- private static final CopyOnWriteArrayList<String> defaultResources =
- new CopyOnWriteArrayList<String>();
- /**
- * The value reported as the setting resource when a key is set
- * by code rather than a file resource.
- */
- static final String UNKNOWN_RESOURCE = "Unknown";
- /**
- * Stores the mapping of key to the resource which modifies or loads
- * the key most recently
- */
- private HashMap<String, String> updatingResource;
- static{
- //print deprecation warning if hadoop-site.xml is found in classpath
- ClassLoader cL = Thread.currentThread().getContextClassLoader();
- if (cL == null) {
- cL = Configuration.class.getClassLoader();
- }
- if(cL.getResource("hadoop-site.xml")!=null) {
- LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
- "Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
- + "mapred-site.xml and hdfs-site.xml to override properties of " +
- "core-default.xml, mapred-default.xml and hdfs-default.xml " +
- "respectively");
- }
- addDefaultResource("core-default.xml");
- addDefaultResource("core-site.xml");
- }
- /**配置文件解析后的键值对**/
- private Properties properties;
- /**用于记录通过set()方式改变的配置项,而不是通过加载配置资源解析得到的变量**/
- private Properties overlay;
- private ClassLoader classLoader;
- {
- classLoader = Thread.currentThread().getContextClassLoader();
- if (classLoader == null) {
- classLoader = Configuration.class.getClassLoader();
- }
- }
- /** A new configuration. */
- public Configuration() {
- this(true);
- }
- /** A new configuration where the behavior of reading from the default
- * resources can be turned off.
- *
- * If the parameter {@code loadDefaults} is false, the new instance
- * will not load resources from the default files.
- * @param loadDefaults specifies whether to load from the default files
- */
- public Configuration(boolean loadDefaults) {
- this.loadDefaults = loadDefaults;
- updatingResource = new HashMap<String, String>();
- synchronized(Configuration.class) {
- REGISTRY.put(this, null);
- }
- }
- //正则表达式对象,包含正则表达式\$\{[^\}\$\ ]+\},u0020是unicode中标识空格的十六进制
- private static Pattern varPat = Pattern.compile("\\$\\{[^\\}\\$\u0020]+\\}");
- //最多做20次扩展
- 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()方法的代码如下:
- 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);
- }
- }
总结
Configuration类在Hadoop的Common包中,它是所有Hadoop功能的基石,所以了解Hadoop首先应该知道Configuration类的作用与构造。上面介绍了Configuration的一些主要的变量与方法,可以为后面的其他源码分析打下坚实的基础。
Reference
《Hadoop技术内幕:深入解析Hadoop Common和HDFS架构设计与实现原理》
原文地址:点击打开链接