1 HDFS
如上圖所示,HDFS也是基於Master/Slave的結構。分NameNode、SecondaryNameNode、DataNode這幾個角色。
NameNode:是Master節點,是大領導。管理數據塊映射;處理客戶端的讀寫請求;配置副本策略;管理HDFS的名稱空間;
SecondaryNameNode:是一個小弟,分擔大哥namenode的工作量;是NameNode的冷備份;合併fsimage和fsedits然後再發給namenode。
DataNode:Slave節點,奴隸,幹活的。負責存儲client發來的數據塊block;執行數據塊的讀寫操作。
熱備份:b是a的熱備份,如果a壞掉。那麼b馬上運行代替a的工作。
冷備份:b是a的冷備份,如果a壞掉。那麼b不能馬上代替a工作。但是b上存儲a的一些信息,減少a壞掉之後的損失。
fsimage:元數據鏡像文件(文件系統的目錄樹。)
edits:元數據的操作日誌(針對文件系統做的修改操作記錄)
namenode內存中存儲的是=fsimage+edits。
SecondaryNameNode負責定時默認1小時,從namenode上,獲取fsimage和edits來進行合併,然後再發送給namenode。減少namenode的工作量。
2 讀取數據流程
- 首先調用FileSystem對象的open方法,其實是一個DistributedFileSystem的實例。
- DistributedFileSystem通過rpc獲得文件的第一批block的locations,同一個block按照重複數會返回多個locations,這些locations按照hadoop拓撲結構排序,距離客戶端近的排在前面。
- 前兩步會返回一個FSDataInputStream對象,該對象會被封裝DFSInputStream對象,DFSInputStream可以方便的管理datanode和namenode數據流。客戶端調用read方法,DFSInputStream最會找出離客戶端最近的datanode並連接。
- 數據從datanode源源不斷的流向客戶端。
- 如果第一塊的數據讀完了,就會關閉指向第一塊的datanode連接,接着讀取下一塊。這些操作對客戶端來說是透明的,客戶端的角度看來只是讀一個持續不斷的流。
- 如果第一批block都讀完了, DFSInputStream就會去namenode拿下一批block的locations,然後繼續讀,如果所有的塊都讀完,這時就會關閉掉所有的流。
如果在讀數據的時候, DFSInputStream和datanode的通訊發生異常,就會嘗試正在讀的block的排序第二近的datanode,並且會記錄哪個datanode發生錯誤,剩餘的blocks讀的時候就會直接跳過該datanode。 DFSInputStream也會檢查block數據校驗和,如果發現一個壞的block,就會先報告到namenode節點,然後DFSInputStream在其他的datanode上讀該block的鏡像。
該設計就是客戶端直接連接datanode來檢索數據並且namenode來負責爲每一個block提供最優的datanode, namenode僅僅處理block location的請求,這些信息都加載在namenode的內存中,hdfs通過datanode集羣可以承受大量客戶端的併發訪問。
案例剖析:
讀操作就簡單一些了,如圖所示,client要從datanode上,讀取FileA。而FileA由block1和block2組成。
那麼,讀操作流程爲:
a. client向namenode發送讀請求。
b. namenode查看Metadata信息,返回fileA的block的位置。
block1:host2,host1,host3
block2:host7,host8,host4
c. block的位置是有先後順序的,先讀block1,再讀block2。而且block1去host2上讀取;然後block2,去host7上讀取;
上面例子中,client位於機架外,那麼如果client位於機架內某個DataNode上,例如,client是host6。那麼讀取的時候,遵循的規律是:
優選讀取本機架上的數據。
3 寫數據流程
1.客戶端通過調用DistributedFileSystem的create方法創建新文件。
2.DistributedFileSystem通過RPC調用namenode去創建一個沒有blocks關聯的新文件,創建前, namenode會做各種校驗,比如文件是否存在,客戶端有無權限去創建等。如果校驗通過, namenode就會記錄下新文件,否則就會拋出IO異常。
3.前兩步結束後,會返回FSDataOutputStream的對象,與讀文件的時候相似, FSDataOutputStream被封裝成DFSOutputStream。DFSOutputStream可以協調namenode和datanode。客戶端開始寫數據到DFSOutputStream,DFSOutputStream會把數據切成一個個小的packet,然後排成隊列data quene。
4.DataStreamer會去處理接受data quene,它先詢問namenode這個新的block最適合存儲的在哪幾個datanode裏(比如重複數是3,那麼就找到3個最適合的datanode),把他們排成一個pipeline。DataStreamer把packet按隊列輸出到管道的第一個datanode中,第一個datanode又把packet輸出到第二個datanode中,以此類推。
5.DFSOutputStream還有一個對列叫ack quene,也是由packet組成,等待datanode的收到響應,當pipeline中的所有datanode都表示已經收到的時候,這時akc quene纔會把對應的packet包移除掉。
如果在寫的過程中某個datanode發生錯誤,會採取以下幾步:
1) pipeline被關閉掉;
2) 爲了防止防止丟包ack quene裏的packet會同步到data quene裏;
3) 把產生錯誤的datanode上當前在寫但未完成的block刪掉;
4) block剩下的部分被寫到剩下的兩個正常的datanode中;
5) namenode找到另外的datanode去創建這個塊的複製。當然,這些操作對客戶端來說是無感知的。
6.客戶端完成寫數據後調用close方法關閉寫入流。
7.DataStreamer把剩餘得包都刷到pipeline裏,然後等待ack信息,收到最後一個ack後,通知datanode把文件標視爲已完成。
注意:客戶端執行write操作後,寫完的block纔是可見的,正在寫的block對客戶端是不可見的,只有調用sync方法,客戶端才確保該文件的寫操作已經全部完成,當客戶端調用close方法時,會默認調用sync方法。是否需要手動調用取決你根據程序需要在數據健壯性和吞吐率之間的權衡。
案例剖析:
有一個文件FileA,100M大小。Client將FileA寫入到HDFS上。
HDFS按默認配置。
HDFS分佈在三個機架上Rack1,Rack2,Rack3。
a. Client將FileA按64M分塊。分成兩塊,block1和block2;
b. Client向nameNode發送寫數據請求,如圖藍色虛線①——>。
c. NameNode節點,記錄block信息。並返回可用的DataNode,如粉色虛線②———>。
Block1: host2,host1,host3
Block2: host7,host8,host4
原理:
NameNode具有RackAware機架感知功能,這個可以配置。
若client爲DataNode節點,那存儲block時,規則爲:副本1,同client的節點上;副本2,不同機架節點上;副本3,同第二個副本機架的另一個節點上;其他副本隨機挑選。
若client不爲DataNode節點,那存儲block時,規則爲:副本1,隨機選擇一個節點上;副本2,不同副本1,機架上;副本3,同副本2相同的另一個節點上;其他副本隨機挑選。
d. client向DataNode發送block1;發送過程是以流式寫入。
流式寫入過程,
1>將64M的block1按64k的package劃分;
2>然後將第一個package發送給host2;
3>host2接收完後,將第一個package發送給host1,同時client想host2發送第二個package;
4>host1接收完第一個package後,發送給host3,同時接收host2發來的第二個package。
5>以此類推,如圖紅線實線所示,直到將block1發送完畢。
6>host2,host1,host3向NameNode,host2向Client發送通知,說“消息發送完了”。如圖粉紅顏色實線所示。
7>client收到host2發來的消息後,向namenode發送消息,說我寫完了。這樣就真完成了。如圖黃色粗實線
8>發送完block1後,再向host7,host8,host4發送block2,如圖藍色實線所示。
9>發送完block2後,host7,host8,host4向NameNode,host7向Client發送通知,如圖淺綠色實線所示。
10>client向NameNode發送消息,說我寫完了,如圖黃色粗實線。。。這樣就完畢了。
分析,通過寫過程,我們可以瞭解到:
①寫1T文件,我們需要3T的存儲,3T的網絡流量帶寬。
②在執行讀或寫的過程中,NameNode和DataNode通過HeartBeat進行保存通信,確定DataNode活着。如果發現DataNode死掉了,就將死掉的DataNode上的數據,放到其他節點去。讀取時,要讀其他節點去。
③掛掉一個節點,沒關係,還有其他節點可以備份;甚至,掛掉某一個機架,也沒關係;其他機架上,也有備份。