Hadoop-0.20.2源码学习(3)——Configuration类

参考: JeffreyZhou的博客园
《Hadoop权威指南》第四版

0 前言

在之前源码初窥中,我们已经找到了主要几个的main函数入口,这里我们列一列:

FsShell – mainorg.apache.hadoop.fs.FsShell
NameNode – mainorg.apache.hadoop.hdfs.server.namenode.NameNode
DataNode – mainorg.apache.hadoop.hdfs.server.datanode.DataNode
JobTracker – mainorg.apache.hadoop.mapred.JobTracker
TaskTracker – mainorg.apache.hadoop.mapred.TaskTracker

按照这个顺序去学下吧。

3.1 main函数入口类

在eclipse中我们很方便地就能找到每个模块的对应的main函数,但还是有些不便,为了调试方便,我们再新建三个入口类:

在这里插入图片描述
自建入口类主要是为方便找到,然后这三个类中的代码分别为:

// FsShellEnter.java
import org.apache.hadoop.fs.FsShell;
public class FsShellEnter {
   public static void main(String[] args) throws Exception {
      FsShell.main(new String[]{"-ls"});
   }
}

// NameNodeEnter.java
public class NameNodeEnter {
   public static void main(String[] args) throws Exception {
      org.apache.hadoop.hdfs.server.namenode.NameNode.main(args);
   }
}

// DataNodeEnter.java
public class DataNodeEnter {
   public static void main(String[] args) {
      org.apache.hadoop.hdfs.server.datanode.DataNode.main(args);
   }
}

可以运行一下来测试是否成功,就按照上一篇文末讲的:
启动命令行,运行$ bin/hadoop namenode,然后在eclipse中,打开FsShellEnter.java,运行之,应该可以看到hdfs dfs -ls一样的效果。
反之亦然。

3.2 Configuration类

Configuration类,见字如面,用于读取配置文件的,先看看在程序中是如何使用Configuration类的:

Configuration conf = new Configuration();
String name = conf.get("fs.defaultFS");
System.out.println(name):

可以看出,上述程序就是读出配置文件中fs.defaultFS的值。

小插曲 – new一个实例的过程:

  • 先加载静态变量
  • 再加载非静态成员变量
  • 最后构造函数
3.2.1 静态变量

所以,先看看静态初始化模块:

static {
// 中间省略掉的是  deprecation warning,无光紧要
addDefaultResource("core-default.xml");
addDefaultResource("core-site.xml");
}

可以看到,在构造方法运行之前,会先加载core-default.xml和core-site.xml两个文件,大家应该会想起来,当初在Hadoop分布式搭建的时候,编辑过几个配置文件,其中就有core-site.xml文件,但是没有core-default.xml文件。

有兴趣的同学,可以去打开这两个文件看看,就会发现,两者有一些相同的配置项,而在这个加载时,按照顺序,先加载default,再加载site,有相同的key时,site则会覆盖掉default。

3.2.2 构造函数

打开源码会发现,有三个构造函数:

// 1、调用第二个构造函数,参数为true
public Configuration() {
    this(true);
}

// 2、确定是否加载默认配置
public Configuration(boolean loadDefaults) {
    this.loadDefaults = loadDefaults;
    if (LOG.isDebugEnabled()) {
      LOG.debug(StringUtils.stringifyException(new IOException("config()")));
    }
    synchronized(Configuration.class) {
      REGISTRY.put(this, null);
    }
  }
  
// 3、将指定的Configuration对象重新复制一份
public Configuration(Configuration other) {
	if(LOG.isDebugEnable()) {
	LOG.debug(StringUtils.stringifyException(new IOException("config(config)")));
	}
	this.resources = (ArrayList)other.properties.clone();
	synchronized(other) {
		if(other.properties != null) {
			this.properties = (Properties)other.properties.clone();
		}
		if(other.overlay != null) {
			this.overlay = (Properties)other.overlay.clone();		
		}
	}
	this.finalParameters = new HashSet<String>(other.finalParameters);
	synchronized(Configuration.class) {
		REGISTRY.put(this, null);
	}
}

也就是说,默认是要加载上面两个配置文件的(true),好吧,我就先不管他加进去之后咋处理了,反正知道,在
Configuration conf = new Configuration();
之后,conf类就已经把core-site.xml加载进去了,里面一些内容如下:

<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>
3.2.3 get() 函数

那接下来就研究一下
String name = conf.get("fs.defaultFS");这句是干嘛的吧,咋就返回了我们想要的String name了呢。

// get函数
public String get(String name, String defaultValue) {
    return substituteVars(getProps().getProperty(name, defaultValue));
  }
// getProps函数
private synchronized Properties getProps() {
    if (properties == null) {
      properties = new Properties();
      loadResources(properties, resources, quietmode);
      if (overlay!= null)
        properties.putAll(overlay);
    }
    return properties;
  }
// getProperty()函数为Property中的方法,输入Key,返回value;
// 若不存在key,则返回defaultValue

这里面先说getProps()函数,这里又出来了一个properties,这又是啥呢?不知道大家有没有注意到,上面的core-site.xml中,就有一个标签<property></property>,哈,是不是有种顿悟的感觉。具体见后记。

getProps函数中,对hashtable类型的properties进行判断,如果为空则调用loadResources()方法来加载XML文件中的键值对保存在properties成员变量中,否则直接返回。然后getProperty再根据其key值进行取值。

这里是采用了懒加载的方式,就是说并没有一开始加载配置文件中的数据,而是等要访问时,才进行加载。

接着往下看,用到了loadResource函数:

private void loadResources(Properties properties, ArrayList resources, boolean quiet) {

// 加载defaultResource
    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);
      }
    }
// 加载形参resource的资源
    for (Object resource : resources) {
      loadResource(properties, resource, quiet);
    }
  }

loadResources先加载默认的资源(defaultResources中保存),再加载形参resources对应的资源。其中defaultResources表示通过方法addDefaultResource()可以添加系统默认资源,成员变量resources表示所有通过addRescource()方法添加Configuration对象的资源。

在loadResource()方法中读取XML文件进行加载。loadResource()方法使用了DOM方式处理XML,逻辑比较简单,具体关于DOM加载XML的方式可以查阅其他资料。

通过以上getProps就会获得所有配置信息了,调用其getProperty方法就可以获取需要属性的值了。再传递给substituteVars进行属性扩展。关于属性扩展概念见后记。

捋一捋
  1. 初始化,加载core-site.xml,搁着,没动;
  2. get(),调用getProps()函数,得到resoure中的properties;
  3. 中间再调用getProperties()函数,取得其中的value值;
  4. 最后再使用subtituteVars()函数,修正最后结果。

3.3 总结一下

从最开始的start-all.sh启动脚本开始,一步步往下摸索,发现脚本启动实际上是启动java程序;

然后又按照.sh脚本中的路径,找到各个模块的main函数,并将核心的几个main函数自建了几个入口,方便启动;

在研究核心模块之前,先研究了一下基础的Configuration类的大概原理,知道了就是对于各个配置文件(.xml文件)进行加载,读取。

3.x 后记

3.x.1 Configuration中的Property 和 Resource

resource——各个.xml文件,里面含有一个或多个property;
properties——一个键值对为一个property;

配置文件的根节点是<configuration>,下一层节点是
<property>,每个property都代表一个配置项,有键值对组成,name节点表示该配置项的键,value节点表示值,除了name和value节点,property节点另一个重要的子节点是final,它表示这个键值对是不可覆盖的,是固定不变的,和Java中的final关键字类似。property节点还有个<description>子节点,是对该属性的描述,类似于Java的注释,在程序中不使用。

3.x.2 什么是属性扩展?

举例说明:
如果配置项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。

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值