Hadoop源代码分析 之hadoop配置及启动(1)—–classpath与配置文件

原创文章,转载请指明出处并保留原文url地址

hadoop是基于java平台的,他有自己的启动及管理脚本,他的启动脚本是如何工作,他的程序是如何启动的,启动过程中配置文件如何传递的,他同普通java项目的区别在那里,为什么我们必须用hadoop脚本来启动hadoop系统以及运行我们自定义的程序。从本文开始,我们将正式进入hadoop源代码中,本文重点阐述hadoop启动过程中配置文件的加载,希望通过文章来解答上面的问题。

本文是对分析过程的一个描述,文章后面还会有两个视频文件, 分别是代码静态分析以及动态分析过程,大家根据习惯参考阅读。

系列文章简介,hadoop代表一种新的编程思想,基于hadoop有很多衍生项目,充分利用他们是非常必要的,同时hadoop又是一个复杂系统,若能理解他的工作原理对我们将有非常大的帮助,我们以hadoop 0.1.0版本为基础逐步分析他的基本工作原理、结构、思路等等,本文是系统文章的一部分,系列文章详情参见

http://www.iigrowing.cn/hadoop_src_introduction

希望通过这个能帮助我们理解生产中hadoop。 时间有限,经验不足,疏漏难免,在这里仅分享一些心得,希望对大家能起到一个抛砖引玉的作用,有问题请大家给我留言或者评论等,这样也能对我的工作有很大帮助。感谢您阅读这篇文章!

一.Hadoop文件组成

Hadoop程序解压缩完成后源代码如下图

clip_image001

Bin目录,hadoop脚本,管理及维护hadoop系统,

Conf目录,hadoop配置文件,hadoop系统相关信息等,

Docs目录,文档目录

Lib目录,hadoop依赖的相关jar文件

Src目录,hadoop源代码文件

Webapps目录, hadoop内嵌的webserver的j2ee程序目录

相关文件:

Build.xml文件时hadoop的ant编译配置文件

其他文件略

二.Hadoop基本启动过程

假设hadoop程序安装主目录用${hadoop_home}表示

管理人员可以执行${hadoop_home}\bin\start-all.sh文件启动hadoop系统

这个脚本将根据conf目录中相关配置文件,启动datanode,namenode,tasktracter,jobtracter等程序

Hadoop的几个主要配置文件如下:

clip_image002

那么脚本在启动hadoop程序中,配置文件信息如何传递给java程序,java程序是如何加载的?

本文将以datanode启动过程为例分析hadoop的配置文件传递,然后后续将逐步分析其他启动程序的配置文件的启动情况。

三.程序员角度看待hadoop启动

我们打开hadoop的源代码项目(eclipse的java项目)

clip_image004

如下图,我们打开如上目录及java源代码文件

clip_image006

双击左侧 main函数的节点, 然后右面显示相关源代码, 然后在

“runAndWait(new Configuration());”函数调用处,右击鼠标,弹出相关对话框,选择“open Declaration” 选项, 可以直接使用快捷方式 F3按键,可以快速到达相关源代码处

代码如下:

private static void runAndWait(Configuration conf) throws IOException {

run(conf); // 调用run函数,进行相关业务逻辑(注意这里的参数对象 conf是在哪里创建的????? 这个非常重要,呕!!!)

// Wait for sub threads to exit 等等线程 退出

for (Iterator iterator = subThreadList.iterator(); iterator.hasNext();) {

Thread threadDataNode = (Thread) iterator.next();

try {

threadDataNode.join();

} catch (InterruptedException e) {

if (Thread.currentThread().isInterrupted()) {

// did someone knock?

return;

}

}

}

}

下面我们看一下配置文件是在哪里创建,我们可以回顾一下main函数,如下图

clip_image007

看红色区域,代码,这个代码创建一个匿名的 配置对象,这个配置对象,采用默认的构造函数。

配置类的构造函数如下

/** A new configuration. */

public Configuration() {

defaultResources.add("hadoop-default.xml"); // 添加配置文件,最大问题,这里仅仅有文件名,没有文件路径,系统到哪里去找文件?

finalResources.add("hadoop-site.xml"); // 添加配置文件,同上面有相同问题

}

稍后我们在解释。。。

下面我们看一下run函数(这里run函数同java线程里面的run函数不是同一个类型的,仅仅是名称相同)

/** Start datanode daemons.

* Start a datanode daemon for each comma separated data directory

* specified in property dfs.data.dir

*/

public static void run(Configuration conf) throws IOException {

String[] dataDirs = conf.getStrings("dfs.data.dir"); // 这里面就调用了我们今天需要重点解决的问题,这里调用配置,配置从哪里来的。 Conf对象在哪里来的

 

下面代码不再今天解释之列,今天可以忽略!!!

subThreadList = new Vector(dataDirs.length);

for (int i = 0; i < dataDirs.length; i++) {

DataNode dn = makeInstanceForDir(dataDirs[i], conf);

if (dn != null) {

Thread t = new Thread(dn, "DataNode: "+dataDirs[i]);

t.setDaemon(true); // needed for JUnit testing

t.start();

subThreadList.add(t);

}

}

}

本函数中重点是: String[] dataDirs = conf.getStrings("dfs.data.dir"); 这行语句的相关情况,直接关联hadoop启动过程中,配置文件的相关获取方法。并且这个过程在整个hadoop中都是类似的,仅仅是获取的配置信息不同而已,但是过程,方法都是非常相似的。

上面函数将调用下面的函数,如下

/** Returns the value of the <code>name</code> property as an array of

* strings. If no such property is specified, then <code>null</code>

* is returned. Values are whitespace or comma delimted.

*/

public String[] getStrings(String name) {

String valueString = get(name); // 重点语句,负责初始化相关代码,我们需要跟踪进入相关代码

if (valueString == null)

return null;

StringTokenizer tokenizer = new StringTokenizer (valueString,", \t\n\r\f");

List values = new ArrayList();

while (tokenizer.hasMoreTokens()) {

values.add(tokenizer.nextToken());

}

return (String[])values.toArray(new String[values.size()]);

}

相关函数代码如下:

/** Returns the value of the <code>name</code> property, or null if no

* such property exists. */

public String get(String name) { return getProps().getProperty(name);}

如下图中用特别颜色标注的代码。

clip_image008

getProps()这是个函数调用,函数调用后会返回一个对象。

getProps().getProperty(name);,根据前一个函数返回的对象,然后调用哪个对象的一个函数

下面是getProps函数的代码

private synchronized Properties getProps() {

if (properties == null) { // 请特别注意这里面的代码,当系统构造配置对象后,这个对象默认是null值的,因此将执行下面代码块中语句的

Properties newProps = new Properties(); // 创建一个资源

loadResources(newProps, defaultResources, false, false); // 装载一个资源,本函数中关键代码!!!!

loadResources(newProps, finalResources, true, true);

properties = newProps;

}

return properties;

}

下面是 配置类中properties 的定义,如下:

private Properties properties; // java中对象默认没有new时 是null的。

装载资源文件函数如下

private void loadResources(Properties props,

ArrayList resources,

boolean reverse, boolean quiet) {

ListIterator i = resources.listIterator(reverse ? resources.size() : 0);

while (reverse ? i.hasPrevious() : i.hasNext()) {

loadResource(props, reverse ? i.previous() : i.next(), quiet); // 循环装入配置文件中的配置,将配置信息存入Properties props中,返回给上层函数。(小问题:java参数一般都是传递值的, 为什么能返回数据给上层???)

}

}

具体装载资源函数如下

private void loadResource(Properties properties, Object name, boolean quiet) {

try {

DocumentBuilder builder =

DocumentBuilderFactory.newInstance().newDocumentBuilder();

Document doc = null;

// 我们目前的程序默认进入的,是走第一个if分支,即name是string类型的,相关情况可以在动态分析中获取到

if (name instanceof String) { // a CLASSPATH resource

URL url = getResource((String)name); // 从classpath路径中加载一个命名的资源,一般为文件等

if (url != null) {

LOG.info("parsing " + url);

doc = builder.parse(url.toString()); // 解析这个文件

}

} else if (name instanceof File) { // a file resource

File file = (File)name;

if (file.exists()) {

LOG.info("parsing " + file);

doc = builder.parse(file);

}

}

if (doc == null) {

if (quiet)

return;

throw new RuntimeException(name + " not found");

}

// 获取相关配置信息,本是系列文章重点,我们重点研究程序运行脉络,细节都可以忽略!!!!

Element root = doc.getDocumentElement();

if (!"configuration".equals(root.getTagName()))

LOG.severe("bad conf file: top-level element not <configuration>");

NodeList props = root.getChildNodes();

for (int i = 0; i < props.getLength(); i++) {

Node propNode = props.item(i);

if (!(propNode instanceof Element))

continue;

Element prop = (Element)propNode;

if (!"property".equals(prop.getTagName()))

LOG.warning("bad conf file: element not <property>");

NodeList fields = prop.getChildNodes();

String attr = null;

String value = null;

for (int j = 0; j < fields.getLength(); j++) {

Node fieldNode = fields.item(j);

if (!(fieldNode instanceof Element))

continue;

Element field = (Element)fieldNode;

if ("name".equals(field.getTagName()))

attr = ((Text)field.getFirstChild()).getData();

if ("value".equals(field.getTagName()) && field.hasChildNodes())

value = ((Text)field.getFirstChild()).getData();

}

if (attr != null && value != null)

properties.setProperty(attr, value);

}

} catch (Exception e) {

LOG.severe("error parsing conf file: " + e);

throw new RuntimeException(e);

}

}

这个类是上面调用的 加载资源的实现方法

/** Returns the URL for the named resource. */

public URL getResource(String name) {

return classLoader.getResource(name);

}

好了,我们已经基本分析完成启动的基本过程, 万里长征我们终于走出第一步,胜利就在我们脚下。

神秘面纱终于揭开一角,一切即将在眼前展现!!!!!

 

 

hadoop-启动-配置文件classpath-静态分析_x264.rar (17.63M, 2012年11月30日 09:56 到期)

进入下载页面

hadoop-启动-配置文件classpath-动态分析_x264.rar (39.41M, 2012年11月30日 09:56 到期)

进入下载页面

参考文章
Hadoop源代码分析 之Datanode工作原理(5)—–拷贝文件过程总结

Hadoop源代码分析 之Datanode工作原理(4)—–拷贝本地文件到hadoop的过程

Hadoop源代码分析 之Datanode工作原理(3)—–datanode工作过程总结

hadoop源代码介绍

Hadoop源代码分析 之Datanode工作原理(2)—–datanode基本工作过程

Hadoop源代码分析 之Datanode工作原理(1)—–datanode启动过程代码分析

Hadoop源代码分析 之hadoop配置及启动(4)—–启动过程汇总

Hadoop源代码分析 之hadoop配置及启动(3)—–classpath与hadoop主要组件启动过程

Hadoop源代码分析 之hadoop配置及启动(2)—–classpath与启动shell脚本

Hadoop源代码分析 之hadoop配置及启动(1)—–classpath与配置文件

Hadoop源代码分析 之hadoop源代码项目(1)—–创建eclipse下java项目

Hadoop源代码分析 之环境配置(2)—–虚拟机ip配置

Hadoop源代码分析 之环境配置(1)—–hadoop虚拟机配置

Hadoop源代码分析 之概念介绍(2)—–初学者眼中的hadoop

Hadoop源代码分析 之概念介绍(1)—–服装加工,火车货物检查与hadoop

调试eclipse下hadoop的map reduce程序

hadoop 相关视频下载地址: http://pan.baidu.com/share/link?shareid=223046310&uk=3073578852

发表评论