Hdfs 的基礎架構
如上圖所示。 默認情況下,Hdfs
由一個 Namenode
和多個 DataNode
組成。
hdfs
作為一個分布式文件存儲系統,他的文件路徑和文件內容是相互隔離的。 文件路徑信息保存在 NameNode
中,文件內容則分布式的保存在 DataNode
中。
也就是說對于一個大文件,它可能被根據其文件大小切割成多個小文件進行存儲,同時這些小文件可能被分布式的存儲在不同的DataNode
中。
當Client希望獲取這個文件時,則需要先根據文件路徑從 NameNode
中獲取他對應的區塊在不同的 DataNode
中的信息,然后才能夠從 DataNode
中取出數據,還原成一個大文件。
具體的代碼邏輯會在之后的讀寫操作中介紹,這里我們先看看 hdfs 集群的啟動流程。
Hdfs 集群啟動流程
默認情況下,我們通過 ${HADOOP_HOME}/sbin/start-dfs.sh
啟動整個 Hdfs
集群。
hdfs namenode "${HADOOP_HDFS_HOME}/bin/hdfs" --workers --config "${HADOOP_CONF_DIR}" --hostnames "${NAMENODES}" --daemon start namenode ${nameStartOpt}
hdfs datanode "${HADOOP_HDFS_HOME}/bin/hdfs" --workers --config "${HADOOP_CONF_DIR}" --daemon start datanode ${dataStartOpt}
hdfs secondarynamenode "${HADOOP_HDFS_HOME}/bin/hdfs" --workers --config "${HADOOP_CONF_DIR}" --hostnames "${SECONDARY_NAMENODES}" --daemon start secondarynamenode
hdfs journalnode "${HADOOP_HDFS_HOME}/bin/hdfs" --workers --config "${HADOOP_CONF_DIR}" --hostnames "${JOURNAL_NODES}" --daemon start journalnode
hdfs zkfc "${HADOOP_HDFS_HOME}/bin/hdfs" --workers --config "${HADOOP_CONF_DIR}" --hostnames "${NAMENODES}" --daemon start zkfc
在 start-dfs.sh
文件中,我們看到shell文件通過執行hdfs
命令先后啟動 NameNode
、 DataNode
、 SecondaryNameNode
、 JournalNode
。
hdfs
也是一個shell文件,通過文本應用打開后,我們看到在該文件的執行邏輯如下:
節點類型和Java類的一一對應
在hdfs
文件中可以看到,傳入的第一個參數對應著需要啟動節點的類型,使用 hdfscmd_case
根據節點類型可以找到對應需要執行的Java類,節點對應表如下。
節點類型 | Java類 |
---|---|
namenode | org.apache.hadoop.hdfs.server.namenode.NameNode |
datanode | org.apache.hadoop.hdfs.server.datanode.DataNode |
secondarynamenode | org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode |
journalnode | org.apache.hadoop.hdfs.qjournal.server.JournalNode |
Tips:類似datanode根據是否是secure mode會有多個Java類對應,但這里只列出最常用的類
為遠程機器啟動hdfs
通過解析節點類型,hdfs
找到了對應的Java類。接下來就應該是啟動并執行這個Java類。
但是如果在啟動命令中包含了 --worker
參數,就代表著需要鏈接到其他節點機器上執行命令。在處理傳參的時候,發現--worker
后,會將 ${HADOOP_WORKER_MODE}
設置為 true, 從而執行 hadoop_common_worker_mode_execute
(對應的方法邏輯在 hadoop-functions.sh
文件中)。在 hadoop_common_worker_mode_execute
中,通過讀取配置文件信息,獲取到真實運行設備的hostname
,通過ssh
執行一個不帶--worker
選項的hdfs
命令,使得hostname
對應的節點機器能夠啟動hdfs
節點。
注意:如果需要通過這種方法啟動遠程機器,需要將啟動機器的.ssh
公鑰加入被啟動機器的信任列表中: cat target/id_rsa.pub >> ~/.ssh/authorized_keys
,否則無法直接鏈接上對應設備
本地啟動hdfs
我們通過ssh
讓遠程設備啟動hdfs節點時,會傳遞過去一個不帶--worker
的可執行命令。
因此通過hdfs
啟動節點時,運行邏輯和之前保持一致,只是${HADOOP_WORKER_MODE}
不為true
。此時通過判斷是否需要在后臺執行命令,我們分別通過 hadoop_daemon_handler
或者 hadoop_java_exec
在前臺或后臺啟動對應的Java任務。
這里需要留意的是,在通過 hdfscmd_case
解析節點指令時,或默認將 ${HADOOP_DAEMON_MODE}
設置為 true
,使得各個節點默認在后臺運行。
NameNode 啟動邏輯
NameNode
在整個hdfs
中扮演著一個很重要的角色,他負責整個文件系統的路徑和數據的管理工作,比較類似 Unix 系統中的 inode table。
Client 通過 NameNode
獲取到指定路徑的分布式文件的 metadata,找到存放在 DataNode
的數據位置,就像從 inode talbe中獲取 inode 編號,然后才能去訪問具體的分布式文件。
從上一小節的表格中,我們看到 NameNode 對應的啟動類是 org.apache.hadoop.hdfs.server.namenode.NameNode
,接下來我們通過走讀NameNode::main
具體查看節點的啟動流程。
由于 NameNode
的啟動參數很多,類似 FORMAT
,CLUSTERID
,IMPORT
等等啟動參數并不會被用來啟動 NameNode
。為了簡單起見,這里只針對默認啟動邏輯 REGULAR
進行分析,啟動流程如下:
如圖所示,對于REGULAR
模式而言,NameNode
節點主要啟動了下面三個組件:
NameNodeHttpServer
startHttpServer
會啟動一個 NameNodeHttpServer
。
NameNodeHttpServer
是一個基于 jetty 服務器的簡單封裝,提供給使用人員一個簡單的節點狀態查詢頁面,默認的綁定端口是 :9870
,靜態頁面代碼路徑是 webapps/namenode
,這一部分由于不涉及到核心邏輯,不做介紹。
FSNamesystem
loadNamesystem
會構建一個 FSNamesystem
對象,FSNamesystem
是 NameNode
中最核心的一個模塊,負責處理維護整個分布式文件系統。詳細的處理邏輯會在后續的章節做仔細介紹。
RPC.Server
createRPCServer
會啟動一個 RPC.Server
線程。
RPC.Server
負責處理 hdfs 集群中的內部通信,在 RPC.Server 中綁定了一個 socket 端口,利用protobuf
的序列化框架進行數據傳輸,關于 RPC.Server 的交互邏輯會在下一章中做詳細介紹,這里可以簡單理解為一個使用socket通信的rpc訪問框架。
if (serviceRpcAddr != null) {
serviceRpcServer = new RPC.Builder(conf).build();
}
if (lifelineRpcAddr != null) {
lifelineRpcServer = new RPC.Builder(conf).build();
}
clientRpcServer = new RPC.Builder(conf).build()
在NameNodeRpcServer
的構造方法中可能會構造 serviceRpcServer
, lifelineRpcServer
和 clientRpcServer
三種 RPC.Server
。
DataNode 啟動邏輯
DataNode
在整個hdfs
框架中負責具體的文件存儲。
我們知道DataNode
的啟動類是org.apache.hadoop.hdfs.server.datanode.DataNode
他的啟動邏輯如上圖所示。和NameNode
類似,在DataNode
中同樣啟動了一個基于 jetty Server的 HTTP 服務器,負責向使用人員展示節點狀態;同樣還有一個基于 RPC.Server 的 socket 鏈接負責 hdfs集群 的內部通信。
DataXceiverServer
initDataXceiver
會啟動一個 DataXceiverServer
類。
DataXceiverServer
負責接收通過TCP協議傳輸過來的文件數據,會在下一章介紹 hdfs 的文件傳輸的時候做介紹,這里先略過
DatanodeHttpServer
DatanodeHttpServer
和 NameNodeHttpServer
一樣,也是一個基于 jetty 服務器的封裝,負責向使用人員提供當前Datanode
的節點狀態信息,默認的啟動端口是 :9864
,靜態頁面代碼路徑 webapps/datanode
,同樣這一部分不設計核心邏輯,不做介紹
RPC.Server
DataNode
中也存在一個RPC.Server
對象,負責維持節點間通信,在下一篇文章中會做詳細介紹。
BlockPoolManager
由于在hdfs
中文件路徑和文件內容是相互隔離的,在DataNode
負責存放分布式的文件內容,但是對于DataNode
自身并不知道自己的文件名,只有一個唯一的 blockId
用以定位文件信息。
在 BlockPoolManager
中,DataNode
會定期上報當前節點的 blockId
列表,以便告知NameNode
節點中擁有的文件內容。具體的邏輯會在 hdfs的文件傳輸的時候做介紹。
總結
在本文中,沒有過多的對源碼進行分析,只簡單的介紹了 NameNode
和 DataNode
兩個節點的啟動邏輯。
原因很簡單,每個子組件的內容都需要結合 NameNode
和 DataNode
甚至還需要 Client
三者結合來講解。而且每個子組件的邏輯也會比較復雜,已經無法放在一個小節里進行講述。
所以,本文中只有 start-dfs.sh
的啟動邏輯和 NameNode
與 DataNode
的關鍵組件構成。