《GTAOL》卡單人戰局圖文教程
HDFS的讀取過程
HDFS的讀取流程大致有以下幾個步驟:
(1)客戶端通過呼叫FileSystem物件的open()來開啟希望讀取的檔案。對於HDFS平臺來說,這個物件是DistributedFileSystem類的是一個例項,所以實際呼叫的是DistributedFileSystem物件的open方法。(DistributedFileSystem類是FileSystem類的一個子類)
public class DistributedFileSystem extends FileSystem{
…………
}
(2)DistributedFileSystem例項通過呼叫RPC介面ClientProtocol.getBlockLocations()方法向名稱節點NameNode獲取該HDFS檔案起始塊的位置。對於每一個Block,NameNode返回存有該Block副本的DataNode地址,同一Block按照副本數會返回多個位置,這些位置按照Hadoop叢集拓撲結構排序,距離客戶端近的排在前面。
其中DistributedFileSystem的open方法會返回一個FSDataInputStream物件給Client以便讀取資料塊,FSDataInputStream是一個DFSInputStream的裝飾類,真正進行資料塊讀取操作的是DFSInputStream物件。
FSDataInputStream物件是一個支援檔案定位的輸入流。DFSInputStream物件管理著DataNode和NameNode的I/O。
(3)然後客戶端通過DFSInputStream物件反覆呼叫read()方法從最優的(與客戶端節點距離最近)DataNode節點上讀取資料塊,資料塊會以資料包(packet)為單位從資料節點通過流式介面傳遞到客戶端,當一個數據塊讀取完畢時,其會再次呼叫ClientProtocol.getBlockLocations()獲取檔案的下一個資料塊位置資訊,並建立和這個新的資料塊的最優DataNode之間的連線,然後HDFS客戶端就會繼續讀取該資料塊了。如果該Client本身就是一個存有該塊副本的DataNode,便從本地DataNode中讀取。
(4)一旦客戶端完成讀取,就對FSDataInputStream呼叫close()方法關閉檔案讀取的輸入流。
在讀取資料的時候,如果DFSInputStream在於DataNode通訊的時候遇到錯誤,會嘗試從這個塊的另外一個最鄰近DataNode讀取資料,同時它也會記住那個故障DataNode,以保證以後不會反覆讀取該節點上後續的塊。DFSInputStream也會通過校驗和確認從DataNode發來的資料是否完整。如果發現有損壞的塊,DFSInputStream會試圖從其他的DFSInputStream讀取其副本,也會將損壞的塊通知給NameNode。
HDFS的寫流程
HDFS的資料寫流程大致有以下幾個步驟:
(1)客戶端通過對DistributedFileSystem物件呼叫create()方法來新建檔案。
(2)DistributedFileSystem對NameNode建立一個RPC呼叫,在檔案系統的名稱空間建立一個新的檔案,此時該檔案還沒有與之關聯的資料塊。Namenode執行各種不同的檢查以確保這個檔案不存在以及客戶端有新建該檔案的許可權。如果這些檢查全部通過,Namenode就會生成一個新的檔案記錄;否則,檔案建立失敗並向client丟擲一個IOException異常。
DistributedFileSystem向客戶端返回一個FSDataOutputStream物件,由此客戶端可以開始寫入資料。就像讀取事件一樣,FSDataOutputStream封裝了一個DFSOutputStream物件,該物件負責處理與DataNode和Namenode之間的通訊。
(3)在Client寫入資料時,DFSOutputStream將它分成一個個的資料包,寫入內部的佇列,稱為資料佇列。其中DataStreamer處理資料佇列,它的責任是挑選出適合儲存資料副本的一組DataNode,並據此要求Namenode來分配新的資料塊。這一組DataNode構成一個傳輸管道-----我們假設副本數為3,那麼此時傳輸管道中有三個節點。
DataStreamer將資料包流式傳輸到管道中的第一個DataNode,該DataNode儲存資料包並將它傳送到傳輸管道中的第二個DataNode。同樣,第二個DataNode儲存該資料包並且傳送到傳輸管道中的第三個DataNode。
(4)DFSOutputStream也維護著一個內部資料佇列來等待DataNode的收到確認回執,這個佇列被稱為“確認佇列(ack queue)”。只有收到管道中所有DataNode的確認回執後,該資料包才會從資料佇列刪除。
(5)如果任何DataNode在資料寫入期間發生故障,則執行以下操作:
- 首先關閉傳輸管道,確認把佇列中的所有資料包都添加回資料佇列的最前端,以確保故障節點下游的DataNode不會漏掉任意一個包。
- 為儲存在另一正常DataNode的當前資料塊制定一個新的標識,並將該標識傳給Namenode,以便故障節點DataNode在恢復後可以刪除儲存的部分資料塊。
- 從傳輸管道中刪除故障資料節點,基於正常的DataNode構建一條新的傳輸管道。把餘下的資料塊寫入傳輸管道中正常的DataNode。Namenode注意到塊副本量不足時,會在另一個節點上建立一個新的副本。後續的資料塊繼續正常接收處理。
- 只要寫入了dfs.replication.min引數設定的副本(預設是1)數量,寫操作就是成功的。並且這個塊會在叢集中被非同步複製,直到其滿足目標副本數(dfs.replication 預設值為3)。
(6)client完成資料的寫入後,就會在資料輸出流中呼叫close()方法。該操作將剩餘的所有資料包寫入DataNode傳輸管道中,然後等待確認。
(7)收到所有的等待確認以後,客戶端會告知NameNode檔案已經寫入完成,此時由於Namenode節點已經知道檔案由哪些塊組成(因為DataStreamer之前已經請求分配了資料塊),所以它只需在返回成功前等待塊進行最小量的複製。
副本佈局策略
Hadoop的預設佈局策略是在執行客戶端的節點上放第1個副本(如果客戶端執行在叢集之外,就隨機選擇一個節點,不過系統會避免挑選那些儲存太滿或太忙的節點。)
第2個副本放在與第1個副本不同且隨機另外選擇的機架的節點上(離架)。第3個副本與第2個副本放在相同的機架,且隨機選擇另一個節點。其他副本放在叢集中隨機的節點上,不過系統會盡量避免相同的機架放太多副本。
之所以這麼佈局,是為了實現很好的負載均衡以及提供一個更好的穩定性,包括以下幾點:
- 寫入頻寬:寫入操作只需要遍歷一個交換機。
- 讀取效能:可以從兩個機架上選擇讀取。
- 叢集中塊的均勻分佈:客戶端只在本地機架上寫入一個塊。
參考:《Hadoop權威指南》