1. 程式人生 > >Hadoop-0.20.2原始碼學習(1)——原始碼初窺

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函式的入口,那麼,接下來就順著這些入口去探索吧。