Hadoop 系列 - HDFS#
一、HDFS 的啟動過程#
- 加載文件的元信息
- 加載日誌文件
- 設置檢查點
- 進入安全模式。作用是:檢查數據塊的副本率,冗餘度是否達到要求
二、HDFS 的運行機制#
用戶文件會被切塊後存儲在多台 DataNode 伺服器當中,並且每個文件在整個集群中存放多個副本,可以提高數據的安全性
三、基本架構#
-
NameNode: 整個文件系統的管理節點,負責記錄文件如何被切分成 Block 數據塊,同時記錄這些數據塊的存儲信息
- Fsimage: 元數據存儲在硬盤上的鏡像文件
- edits: 系統操作的日誌記錄文件
- Fstime: 保存最近一次 checkpoint 的時間
- seen_txid: 最後一個 edites 的數字
- version
-
Secondary NameNode: 輔助後台程序 (不是 NameNode 的容災節點),與 NameNode 進行通信,以便定期保存 HDFS 元數據的快照
-
DataNode: 數據節點,負責把 HDFS 數據塊讀寫到本地的文件系統
HDFS 不適合存儲小文件的原因,每個文件都會產生元信息,當小文件多了之後元信息也就多了,對 namenode 會造成壓力
聯邦 HDFS#
每個 NameNode 維護一個命名空間,不同 NameNode 之間的命名空間相互獨立,每個 DataNode 需要註冊到每個 namenode 上
-
多個 NN 共用一個集群 DN 的存儲資源,每個 NN 都可以單獨對外提供服務。
-
每個 NN 都會定義一個存儲池,有單獨的 id,每個 DN 都為所有存儲池提供存儲。
-
DN 會按照存儲池 id 向其對應的 NN 匯報塊信息,同時,DN 會向所有 NN 匯報本地存儲可用資源情況。
-
如果需要在客戶端方便的訪問若干個 NN 上的資源,可以使用客戶端掛載表,把不同的目錄映射到不同的 NN,但 NN 上必須存在相應的目錄
四、NameNode 工作機制#
HDFS 讀寫 -> 滾動記錄日誌 -> SN 向 NN 詢問是否需要 checkPoint -> 時間到了 (60 分鐘) 或者 edits 數據滿了觸發檢查點 -> SN 請求執行 checkPoint -> NN 拷貝 edits 文件和 fsimag 文件到 SN -> SN 端合併 editslog 到 fsimage -> SN 將計算合併完的 fsimage 同步到 NN
五、DataNode 工作機制#
- DataNode 啟動向 NN 註冊,然後定期匯報 block 數據塊的信息
- NN 和 DataNode 之間通過心跳檢測機制,心跳 1 次 / 3s,如果超過 10 分鐘未收到心跳則認為節點不可用
六、HDFS 讀數據流程#
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(conf);
Path file = new Path("demo.txt");
FSDataInputStream inStream = fs.open(file);
String data = inStream.readUTF();
System.out.println(data);
inStream.close();
- 客戶端初始化
FileSystem
對象,調用open()
方法獲取一個DistributedFileSystem
對象 - DistributedFileSystem 透過 RPC 向 NN 請求獲取第一批 block locations
- 前兩部會生成一個 FSDataInputStream, 該對象會被封裝成 DFSInputStream 對象
- 客戶端調用
read()
方法,DFSInputStream 會找出離客戶端最近的 Datanode 並連接開始讀取此文件的第一個數據塊,數據從 Datanode 傳輸向客戶端 - 當第一個數據塊讀取完畢,DFSInputStream 關閉連接,然後連接下一次數據塊的 DataNode 節點繼續數據傳輸
- 如果在讀數據的時候 DFSInputStream 和 Datanode 數據節點的通訊發生異常,就會嘗試連接下一個包含次數據塊的 DataNode 節點,並且會記錄哪個 datanode 發生錯誤,剩餘的 blocks 讀的時候就會直接跳過該 datanode
- 客戶端讀取數據完畢後調用 close () 方法關閉連接然後將數據寫入到本地文件系統
七、HDFS 寫數據流程#
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(conf);
Path file = new Path("demo.txt");
FSDataOutputStream outStream = fs.create(file);
out.write("Welcome to HDFS Java API !!!".getBytes("UTF-8"));
outStream.close();
- 客戶端調用的
create
方法,創建一個文件輸出流DFSDataOutputStream
對象,向 NN 請求上傳文件 - DistributedFileSystem 透過 RPC 調用 NN 去創建一個沒有 blocks 關聯的新文件條目(Entry), 創建前 NN 會校驗文件是否存在或者是否有權限創建,成功直接先將操作寫入 EditLog (
WAL,write ahead log
),並返回輸出流對象,否則拋出 IO 異常 - 客戶端會首先將文件進行拆分,比如一個 Block 塊 128M,文件有 300M 會被切分為 3 個塊,兩個 128M,一個 44M,然後向 NN 請求 Block 該傳輸到哪些 DataNode 伺服器上
- NN 返回可寫的 DN 節點信息,客戶端和 NameNode 分配的多個 DataNode 構成 pipeline 管道建立起連接,client 端向輸出流對象中寫數據
- 透過 FSDataOutputStream 對象向 DN 寫數據,這些數據會被拆分成一個個小的 packet,然後排成隊列 data quene。客戶端每向第一個 DataNode 寫入一個 packet,這個 packet 便會直接在 pipeline 裡傳給第二個、第三個…DataNode,並不是寫好一個塊或一整個文件後才向後分發
- 每個 DN 寫完一個 block 後向客戶端響應
ack確認信息
, 注意並不是每寫完一個 packet 後就返回確認信息 - 客戶端完成寫數據後調用
close
方法關閉流
補充:
- 客戶端執行 write 操作後,寫完的 block 才是可見的,正在寫的 block 對客戶端是不可見的,只有調用
sync
方法,客戶端才確保該文件的寫操作已經全部完成,當客戶端調用 close 方法時,會默認調用 sync 方法。是否需要手動調用取決你根據程序需要在數據健壯性和吞吐率之間的權衡
寫的過程中錯誤處理:#
寫的過程某個 DN 副本節點錯誤:#
- 首先 pipeline 會被關閉掉
- 將已經發送到管道但還沒有收到確認的 packet 寫回到 data quene, 避免數據丟失
- 然後當前正常工作的 DN 數據節點將會被賦予一個新的版本號(利用 namenode 中租約的信息可以獲得最新的時間戳版本),這樣故障節點恢復後由於版本信息不對,故障 DataNode 恢復後會被刪除
- 刪除故障節點,選擇正常的 DN 數據節點重新建立管道,然後開始重新寫入數據
- 發現副本不足,NN 會去其他的 DN 節點上創建一個新的副本
寫的過程客戶端崩潰解決:#
當數據寫入過程中客戶端異常退出時,同一 block 數據塊的不同副本可能存在不一致的狀態。 選擇某一副本作為主數據節點,協調其他數據節點,NN 會透過租約機制 (lease) 找到所有 DN 副本節點擁有這個數據塊信息的最小 block 長度,然後將該數據塊恢復到他們中的最小長度
詳細參考: HDFS 恢復過程 1
八、HDFS 副本機制#
第一副本:如果上傳節點是 DN,則上傳該節點;如果上傳節點是 NN,則隨機選擇 DN
第二副本:放置在不同機架的 DN 上
第三副本:放置在與第二副本相同機架的不同 DN 上
九、HDFS 安全模式#
安全模式是 HDFS 的一種工作狀態,處於安全模式的狀態下,只向客戶端提供文件的只讀視圖,不接受對命名空間的修改
- NN 啟動時首先會將 fsimage 加載到內存中,然後執行 edits 日誌記錄中的操作,當在內存中成功建立文件系統元數據的映射後,新建一個 fsimage 和一個 edits 空的編輯日誌文件,這個時候 NN 運行在安全模式
- 在此階段 NN 通過 DN 收集信息,對每個文件的數據塊進行統計,當確認滿足最小副本條件時,即一定比例的數據塊都達到最小副本數,就會退出安全模式。當不滿足的時候安排 DN 對對副本數不足的數據塊進行複製,直至達到最小副本數
- 在啟動一個剛剛格式化的 HDFS 時不會進入安全模式,因為沒有數據塊
退出安全模式:hdfs namenode -safemode leave
十、HA 高可用機制#
參考: Hadoop NameNode 高可用 (High Availability) 實現解析
基本架構實現#
HDFS 的 HA 高可用透過 zk 保證,基本架構:
-
Active NameNode
和Standby NameNode
: 主備 NameNode 節點,只有處於 Active 的主 NameNode 節點對外提供服務 -
共享存儲系統
: 保存了 NN 運行過程中產生的元數據,主備 NN 透過共享存儲系統實現元數據同步,在進行主備切換的時候新的 NN 只有確認元數據完全同步後才能對外提供服務 -
主備切換控制器 ZKFailoverController
: ZKFC 作為獨立的進程運行,能及時監測到 NN 的健康狀態,當主 NN 出現故障時借助 zk 集群實現主備自動選舉切換 -
DataNode: DataNode 需要同時向主備 NN 上傳數據塊信息保證 HDFS 的數據塊和 DataNode 之間的映射關係同步
-
Zookeeper 集群:為主備切換控制器提供主備選舉支持
主備切換實現#
NameNode 主備切換主要由ZKFailoverController
、HealthMonitor
和 ActiveStandbyElector
這 3 個組件來協同實現:
-
主備切換控制器 ZKFailoverController 啟動的時候會創建 HealthMonitor 和 ActiveStandbyElector 這兩個主要的內部組件,ZKFailoverController 在創建 HealthMonitor 和 ActiveStandbyElector 的同時,也會向 HealthMonitor 和 ActiveStandbyElector 註冊相應的回調方法。
-
HealthMonitor 主要負責檢測 NameNode 的健康狀態,如果檢測到 NameNode 的狀態發生變化,會回調 ZKFailoverController 的相應方法進行自動的主備選舉。
-
ActiveStandbyElector 主要負責完成自動的主備選舉,內部封裝了 Zookeeper 的處理邏輯,一旦 Zookeeper 主備選舉完成,會回調 ZKFailoverController 的相應方法來進行 NameNode 的主備狀態切換
如圖,流程分析:
- HealthMonitor 初始化完成後會啟動內部線程定時調用對應 NameNode 的 HAServiceProtocol RPC 接口的方法,對 NameNode 的健康狀態進行檢測
- 當檢測到 NN 狀態發生變化時,回調 ZKFailoverController 的相應方法進行處理
- ZKFailoverController 監測到需要進行主備切換時使用 ActiveStandbyElector 進行處理
- ActiveStandbyElector 與 ZK 進行交互完成自動選舉,然後回調 ZKFailoverController 的相應方法通知當前 NN
- ZKFailoverController 調用對應 NameNode 的 HAServiceProtocol RPC 接口的方法將 NameNode 轉換為 Active 狀態或 Standby 狀態