參考自《Hadoop權威指南》 ![http://www.cnblogs.com/swanspouse/p/5137308.html]
HDFS讀文件過程:
- 客戶端通過調用FileSystem對象的open()來讀取希望打開的文件。對於HDFS來說,這個對象是分布式文件系統的一個實例。
- DistributedFileSystem通過RPC來調用namenode,以確定文件的開頭部分的塊位置。對於每一塊,namenode返回具有該塊副本的datanode地址。此外,這些datanode根據他們與client的距離來排序(根據網絡集群的拓撲)。如果該client本身就是一個datanode,便從本地datanode中讀取。DistributedFileSystem 返回一個FSDataInputStream對象給client讀取數據,FSDataInputStream轉而包裝了一個DFSInputStream對象。
- 接着client對這個輸入流調用read()。存儲着文件開頭部分塊的數據節點地址的DFSInputStream隨即與這些塊最近的datanode相連接。
- 通過在數據流中反復調用read(),數據會從datanode返回client。
- 到達塊的末端時,DFSInputStream會關閉與datanode間的聯系,然后為下一個塊找到最佳的datanode。client端只需要讀取一個連續的流,這些對於client來說都是透明的。
- 在讀取的時候,如果client與datanode通信時遇到一個錯誤,那么它就會去嘗試對這個塊來說下一個最近的塊。它也會記住那個故障節點的datanode,以保證不會再對之后的塊進行徒勞無益的嘗試。client也會確認datanode發來的數據的校驗和。如果發現一個損壞的塊,它就會在client試圖從別的datanode中讀取一個塊的副本之前報告給namenode。
- 這個設計的一個重點是,client直接聯系datanode去檢索數據,並被namenode指引到塊中最好的datanode。因為數據流在此集群中是在所有datanode分散進行的。所以這種設計能使HDFS可擴展到最大的並發client數量。同時,namenode只不過提供塊的位置請求(存儲在內存中,十分高效),不是提供數據。否則如果客戶端數量增長,namenode就會快速成為一個“瓶頸”。
HDFS寫文件過程
- 客戶端通過在DistributedFileSystem中調用create()來創建文件。
- DistributedFileSystem 使用RPC去調用namenode,在文件系統的命名空間創一個新的文件,沒有塊與之相聯系。namenode執行各種不同的檢查以確保這個文件不會已經存在,並且在client有可以創建文件的適當的許可。如果檢查通過,namenode就會生成一個新的文件記錄;否則,文件創建失敗並向client拋出一個IOException異常。分布式文件系統返回一個文件系統數據輸出流,讓client開始寫入數據。就像讀取事件一樣,文件系統數據輸出流控制一個DFSOutputStream,負責處理datanode和namenode之間的通信。
- 在client寫入數據時,DFSOutputStream將它分成一個個的包,寫入內部隊列,稱為數據隊列。數據流處理數據隊列,數據流的責任是根據適合的datanode的列表要求namenode分配適合的新塊來存儲數據副本。這一組datanode列表形成一個管線————假設副本數是3,所以有3個節點在管線中。
- 數據流將包分流給管線中第一個的datanode,這個節點會存儲包並且發送給管線中的第二個datanode。同樣地,第二個datanode存儲包並且傳給管線中的第三個數據節點。
- DFSOutputStream也有一個內部的數據包隊列來等待datanode收到確認,稱為確認隊列。一個包只有在被管線中所有的節點確認后才會被移除出確認隊列。如果在有數據寫入期間,datanode發生故障, 則會執行下面的操作,當然這對寫入數據的client而言是透明的。首先管線被關閉,確認隊列中的任何包都會被添加回數據隊列的前面,以確保故障節點下游的datanode不會漏掉任意一個包。為存儲在另一正常datanode的當前數據塊制定一個新的標識,並將該標識傳給namenode,以便故障節點datanode在恢復后可以刪除存儲的部分數據塊。從管線中刪除故障數據節點並且把余下的數據塊寫入管線中的兩個正常的datanode。namenode注意到塊復本量不足時,會在另一個節點上創建一個新的復本。后續的數據塊繼續正常接收處理。只要dfs.replication.min的副本(默認是1)被寫入,寫操作就是成功的,並且這個塊會在集群中被異步復制,直到其滿足目標副本數(dfs.replication 默認值為3)。
- client完成數據的寫入后,就會在流中調用close()。
- 在向namenode節點發送完消息之前,此方法會將余下的所有包放入datanode管線並等待確認。namenode節點已經知道文件由哪些塊組成(通過Data streamer 詢問塊分配),所以它只需在返回成功前等待塊進行最小量的復制。
- 復本的布局:需要對可靠性、寫入帶寬和讀取帶寬進行權衡。Hadoop的默認布局策略是在運行客戶端的節點上放第1個復本(如果客戶端運行在集群之外,就隨機選擇一個節點,不過系統會避免挑選那些存儲太滿或太忙的節點。)第2個復本放在與第1個復本不同且隨機另外選擇的機架的節點上(離架)。第3個復本與第2個復本放在相同的機架,且隨機選擇另一個節點。其他復本放在集群中隨機的節點上,不過系統會盡量避免相同的機架放太多復本。
- 總的來說,這一方法不僅提供了很好的穩定性(數據塊存儲在兩個機架中)並實現很好的負載均衡,包括寫入帶寬(寫入操作只需要遍歷一個交換機)、讀取性能(可以從兩個機架中選擇讀取)和集群中塊的均勻分布(客戶端只在本地機架上寫入一個塊)。