HDFS的讀寫流程


HDFS的讀取過程

image-20210618171749457

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的寫流程

image-20210618171731104

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權威指南》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM