Hadoop-0.20.2源码学习(1)——源码初窥

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

0. 为什么选择0.20.2版本

前面学习搭建的Hadoop版本是2.7.6,可是这里为什么要学习0.20.2这么老的版本呢?
hadoop-version因为Hadoop经过这么多年的发展,其源代码已经是一个庞大的代码库了,而我的目的是研究Hadoop最基本,最底层的实现思想,而不是各种各样的插件和功能代码,所以还是找一个早期的版本源码来学习吧。这里贴上一个Apache的软件大全:

archive.apache.org/dist

在里面几乎包括了Apache大多数的项目,按字母排序,往下找就可以看到hadoop文件夹了,在common/文件夹内,就可以看到各个版本的hadoop了,本例中下载的hadoop-0.20.2.tar.gz文件。

在这里插入图片描述

然后将其解压到一个专门的文件夹就行了,用来进行学习研究。

1.1 源文件目录

hadoop根目录

根据本文的参考资料,整理如下:

目录/文件 说明
bin 下面存放着可执行的sh命名,所有操作都在这里
conf 配置文件所在目录
ivy Apache Ivy是专门用来管理项目的jar包依赖的,这个是ivy的主要目录
lib 引用的库文件目录,里面存放用到的jar包
src 这个里面就是主要的源码了
build.xml 用于编译的配置文件。 编译我们用的是ant(还未编译暂时无build文件夹,后面会讲到)
CHANGES.txt 文本文件,记录着本版本的变更历史
ivy.xml Ivy的配置文件
LICENSE.txt 文件本文件
NOTICE.txt 文本文件,记录着需要注意的地方
README.txt 说明文件

src目录

进入src目录,可以看见:
在这里插入图片描述

目录/文件 说明
ant 为ant命令编写的扩展指定
build 就存放一个打包信息文件
c++ linux下amd64-64位系统以及i386-32位系统提供的库文件集合
contrib 是开源界或第三方为hadoop编写的一些扩展程序,如eclipse插件等
core Hadoop的核心代码
docs 文档
examples 示例程序
hdfs HDFS模块的代码
marped MapReduce模块代码
test 测试程序
tools 工具集
webapps 网页管理工具的代码,主要是jsp文件
fixFontsPath.sh 用于修正字体路径的批处理命令
saveVersion.sh 用于生成打包信息文件批处理命令

1.2 编译

对于刚下载的源代码,是无法直接运行的,需要编译,这个主要就是antbuild.xml的作用了。
先修改一个build.xml,打开,将其中的版本号改为:0.20.2,如:
build-xml

用ant进行编译,至于这是什么操作,我不太懂,也没去深究,想着暂时能搭起来框架再说,所以直接在命令行里输入(这里前提是执行路径在已经解压后的hadoop-src文件夹中):

~/hadoop-src$ ant
~/hadoop-src$ ant jar
~/hadoop-src$ ant examples

这时候,就好自动去下载jar包,进行编译了,好像时间不短,到最后出现下面这样就行了:

build

这时,在根目录可以发现,多了一个build文件夹,这个文件夹下,我们发现有大量的子文件夹,再深入看,可以找到了N多个.class文件。那这个正是java程序的编译产出物。

关于ant

我们知道,一个.java程序对应一个.class文件,手动的话用javac来编译。我们要将这么多的java文件都要编译成一个个的.class文件,敲javac命令肯定是不行的,我们得找个打包处理的办法。这个就是ant。简单的说ant就是将编译命名进行打包处理的程序,这个程序有一个配置文件就是build.xml。所以我们进入hadoop根目录后输入了ant后就开始运行了,因为它在当前目录下找到了build.xml文件

1.3 build.xml文件

我们简要的来看一下build.xml。 打开一看,build.xml文件貌似很复杂,有1千8百多行,但不要怕,我们暂不关注代码细节,用面向对象的思想,简单看下结构:

build-1

  1. 可以看到,一上来,定义了一个project,看来这是一个工程,有名称和default属性(default后面看是啥)。
    接下来发现是一堆的property,然后是name-value的健值。应该猜的出,这些就是后面真正执行用的一些变量或参数。

  2. 再往下,看到有这些:
    在这里插入图片描述

看到有target,然后取了个名,字面意思是目标,然后看看子结点,发现是mkdir,好熟悉的字眼,这不是在创建目录么,看下第一个dir是啥,${build.dir}。然后立即跑回上面property中,看下是否有呢?

property

果然,这个就是在编译后的产生的目录,第一步创建之,很正常。

既然这样,这个target就是一个个目标,然后往下拖一下,发现下面的都是一个个的目录,全文搜索一下:
在这里插入图片描述

发现里面有106个。

  1. 继续搜,发现了亮点:
    在这里插入图片描述

这个target(目标)好眼熟,~/hadoop-src$ant jar没错,当时在编译时,输入这个命令后,就产出了一个jar文件。看来这个target就是在形成 jar文件,略看下其子命令,的确就是在生成jar包了。
简单了解了这个target后,就可以继续找找,我们刚才编译时使用的examples命令了。现回想起来,在编译时第一个命令是~/hadoop-src$ant,这个后面没有加参数,所以,有一个默认参数?想到第一行,新建project时,里面有一个default=“complie”
在这里插入图片描述
来查查看,搜索“target name="complie”:

在这里插入图片描述
果然,猜的没错。找到了这个默认目录,然后发现好多target后还有depends,字面意思,依赖吧,然后可以继续找,依赖里面的目录,也是一个个的target。

到这里,也就知道了,这个build.xml文件里面包括了很多子模块,我们在一开始就只使用了默认参数,jar,example,还有很多没用过。

1.4 运行

在前面的学习中,启动Hadoop需要在 Master 节点上执行:

$ start-dfs.sh
$ start-yarn.sh
$ mr-jobhistory-daemon.sh start historyserver

那么,这个start-dfs.shstart-yarn.sh又是什么东东呢?我们先打开bin目录看下:

在这里插入图片描述

我们先观察下所有文件,看到下面8个,很有规律,4个startXXX.sh然后4个stopXXX.sh文件。看来这些就是用户启动和关闭hadoop用的。
可以看到,里面有很多个sh文件,hadoop这个文件,虽然没有后缀,但打开看后,发现跟其他sh文件样,类似的脚本。

在windows中我们知道bat文件,就是将若干个命令放到一个文件中,依次执行,称之为批处理文件。在Linux中,这个bat文件就是sh文件。

1.4.1 start-all.sh文件

在早期的Hadoop版本中,启动Hadoop使用的是start-all.sh命令,知道到了现在已经被换成了start-dfs.shstart-yarn.sh,那我们就先看看start-all.sh这个 all 到底 all 了多少东西。

start-all

直接看主要内容,发现并不多,首先是调用了一下hadoop-config.sh,看字面意思就是配置文件,然后再调用start-dfs.shstart-mapred.sh,然后就没啦。这不就是偷个懒,将两个命令放在一个命令了嘛,没啥干货。
同理,打开stop-all.sh也就是对称的几个命令了:
stop-all

1.4.2 start-dfs.sh文件

那就按照上面的思路来吧,它是靠着start-dfs.sh来伪装自己的,那我们就看看这里面有啥:

start-dfs
这里面乍一看都是相同的命令,其实主要是两条命令hadoop-daemons.shhadoop-daemon.sh,其实这时大家可以打开其他的几个startXX.shstopXX.sh文件,你会发现,所有的操作都又转入了hadoop-daemon.sh和hadoop-daemons.sh这两个命令,同时传入了参数—config stop/start (opt参数),即形式为:

hadoop-daemon(s).sh  --config  $HADOOP_CONF_DIR stop/start  namenode/datanode/...

1.4.3 hadoop-daemons.sh文件

其实,打开hadoop-daemons.sh

hadoop-daemons
可以看到,这一连串下来,最终执行的就是两个东西:hadoop-daemon.shslaves.sh

1.4.4 slaves.sh文件

看到 slaves 文件,可以想到在前期配置Hadoop2.7.6分布式环境的时候,我们也配置了一个slaves文件,里面是用回车换行隔开的slave1、slave2等各个子节点。那么,这两个slaves文件是否有关系呢?来看看:

slaves

看一下代码,第一个if与fi之间就是取得conf文件夹下的slaves文件(就是配置Hadoop分布式环境时,创建的slaves文件),所以第二段代码,for循环slaves中的文件,然后调用ssh命令,调到了子系统中的相应的命令,这里,就完全可以想通了,为什么子系统中部署的hadoop目录需要与主目录相同,然后slaves中配置的是子系统机器的名称。

1.4.5 hadoop-daemon.sh文件

到这里,整个bin目录的脚本,就集中在剩下的两个hadoop-daemon.shhadoop了。胜利在望了。先看hadoop-daemon.sh
在这里插入图片描述

一开始,代码是在取参数,startstop和command,从前面的传入可以看到,startstop参数传的是start和stop,看来是启动和关闭, command是namenode、datanode之类的。
继续往下看:
在这里插入图片描述

对照前面传进来的参数,进行case分类,将start和stop命令分开处理。
在start时,先是创建一个文件夹PID_DIR,具体值可以看上面,然后第一段if,是在判断进程有没有启动,然后最关健是执行

nohup nice -n $HADOOP_NICENESS…. /bin/hadoop。

也就是说归根到底又都是在执行hadoop命令了。这里nohup,是指启动进程后不被卡住,即转为后台进程,又称守护进程,所以该sh文件命名为daemon也不为过。

然后stop段时,把进程进行kill掉。这里有疑问了,启动的命令kill里需要知道进程的PID,而kill里哪里获取呢,在启动时,将启动进程后的pid记录在PID文件夹内,然后kill时就可以跟据这些PID来处理了。机智。

1.4.6 hadoop文件

在执行hadoop命令时,又将namenode、datanode、secondarynamenode等命令传入。所以现在可以打开hadoop命令文件了,(直接跳入重点部分):

在这里插入图片描述

可以看到,这里有大量的if语句,判断条件就是command命令,然后给class和hadoop_opts,赋予不同的值,先不看后面,这里有点java基础的同学可以看到,class的值,像不像java包的名?换句话说,是不是就是class文件的位置?所以,这一个个NameNode之类的主函数应该就是在这找到并执行咯?好,继续往下看:

在这里插入图片描述

哦哟,这里真的是在执行Java程序,可以看到后面的路径是$CLASSPATH $CLASS,这里应该是hadoop目录下的包路径,那就去看看到底是啥吧,比如找到并打开DataNode.java文件,然后搜索是否有main函数:

在这里插入图片描述

perfect!

1.5总结一下

整个hadoop程序,是一个java为主的程序,编译是将.class文件生成在build目录,在运行时,虽然执行的的.sh文件,但在.sh脚本文件中,一步步的执行下去,最终还是执行java命令,执行入口就是各个子程序的函数入口。

1.6 后记

  1. 前面的启动程序我们都是通过命令start-all.sh,在后台启动整个程序,输出内容在log文件夹内,那么,按照上面的推理,可以从命令行单独启动NameNode或者DataNode,可以试一试。在命令行输入bin/hadoop namenode,应该是可以看到启动了namenode程序,日志信息也直接打印在屏幕上了。

  2. 在hadoop.sh文件中,可以看到所有的入口,那就把它整理成一个表,这就是所有的main函数:

命令 入口
namenode org.apache.hadoop.hdfs.server.namenode.NameNode
secondarynamenode org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode
datanode org.apache.hadoop.hdfs.server.datanode.DataNode
fs / dfs org.apache.hadoop.fs.FsShell
jobtracker org.apache.hadoop.mapred.JobTracker
tasktracker org.apache.hadoop.mapred.TaskTracker
job org.apache.hadoop.mapred.JobClient
version org.apache.hadoop.util.VersionInfo
  1. 现在知道hadoop是从哪开始启动的了,也找到了各个main函数的入口,那么,接下来就顺着这些入口去探索吧。

猜你喜欢

转载自blog.csdn.net/qq_41059320/article/details/83445574
今日推荐