🌈hdfs寫入流程(面試重點)
一個文件上傳到hdfs文件系統的簡略過程

1)客戶端通過Distributed FileSystem模塊向NameNode請求上傳文件,NameNode檢查目標文件是否已存在,父目錄是否存在。
2)NameNode返回是否可以上傳。
3)客戶端請求第一個 Block上傳到哪幾個DataNode服務器上。
4)NameNode返回3個DataNode節點,分別為dn1、dn2、dn3。
5)客戶端通過FSDataOutputStream模塊請求dn1上傳數據,dn1收到請求會繼續調用dn2,然后dn2調用dn3,將這個通信管道建立完成。
6)dn1、dn2、dn3逐級應答客戶端。
7)客戶端開始往dn1上傳第一個Block(先從磁盤讀取數據放到一個本地內存緩存),以Packet為單位,dn1收到一個Packet就會傳給dn2,dn2傳給dn3;dn1每傳一個packet會放入一個應答隊列等待應答。
8)當一個Block傳輸完成之后,客戶端再次請求NameNode上傳第二個Block到服務器。(重復執行3-7步)。
一個文件上傳到hdfs文件系統的詳細過程

- hdfs client客戶端調用DistributedFileSystem.create()方法,這個方法會通過RPC(遠程過程調用)的方式調用namenode的create()方法
- namenode在hdfs文件系統創建一個空文件,並將這一個操作記錄在edits.log文件中(因為創建空文件產生了新的元數據)。
- 如果namenode.create()方法沒有拋出異常,就會返回給客戶端一個FSDataInputStream輸出流對象,FSDataInputStream類是對DFSOutputStream類的一個包裝。
- 有了DFSOutputStream輸出流對象后,客戶端就調用方法DFSOutputStream.write()方法,這個方法會通過RPC的方式調用namenode的addBlock()方法,向namenode申請上傳一個數據塊。
- 調用addBlock()方法無異常后,namenode就會返回給客戶端一個LocatedBlock對象,這個對象包含的主要信息是:這個block要存儲在哪三個datanode節點上。
(每次啟動hdfs集群,每個datanode就會通過心跳方式向namenode匯報節點磁盤使用率,然后namenode就可以通過使用情況來決定block存在哪里) - 客戶端根據namenode返回的塊將要存放的位置信息,建立數據流管道pipeline。
- 輸出流DFSOutputSream開始讀取block的數據,然后把數據寫到一個叫做chunk的文件,當chunk文件寫滿512字節后,就會調用關於crc32的校驗方法來進行數據校驗,生成一個4字節的checksum校驗文件。(這就是為什么JavaApi操作hdfs文件系統會生成一個.crc文件)(為什么做校驗?)
- 校驗后,客戶端就將chunk+chumksum兩個文件共516字節存放到一個有64KB容量的package里。package包括三部分(header、checksums、DATA),checksums專門用來存放校驗文件,DATA專門用來存放數據。
- 當package存滿64KB時,就會將package傳到一個data queue數據隊列(可以看成一個個package的隊列)里,
- 然后將數據隊列里的每個package按順序沿着建立的數據流管道傳到第1個datanode節點,再從第1個節點傳到第2個節點,再從第2個節點傳到第3個節點。
- 每個datanode節點接受到package之后,就會對package進行數據校驗,根據數據產生新的校驗值,判斷新的校驗值和傳過來的校驗值是否匹配。校驗正確的結果ACK是反着pipeline方向 返回來的,datanode3--->datanode2-->datanode1。如果校驗通過的話,傳輸就成功了。(每個datanode傳輸情況都正常,ACK才能返回給客戶端)
- 當前正在發送的package不只是沿着數據流管道傳到datanode節點,還會被存放到一個ack queue隊列里。如果package傳輸成功的話,就會刪除ack queue隊列里的該package。如果不成功的話就將ack queue里的package取出來放到data queue的末尾,等待重新傳輸到datanode。
- 如果這個文件還有其它的塊block,則重新執行上面的4-11步驟,直到文件傳輸完成。
- 文件傳輸完成后,datanode就會通過RPC遠程調用namenode的blockReceivedAndDelete(),然后namenode就會更新內存中block和datanode的映射關系,更新文件和block的映射關系。
- 最后關閉pipeline數據流管道,關閉數據流DFSOutputStream.close(),客戶端遠程調用namenode的complete()方法,告知上傳完成。
補充:
為什么做校驗?因為是網絡傳輸,可能數據傳輸會出現異常什么的,需要保證數據的完整和正確性。
循環冗余校驗(Cyclic Redundancy Check, CRC)是一種根據網絡數據包或計算機文件等數據產生簡短固定位數校驗碼的一種信道編碼基技術,主要用來檢測或校驗數據傳輸或者保存后可能出現的錯誤。
CRC32: CRC本身是“冗余校驗碼”的意思,CRC32則表示會產生一個32bit(4字節)的校驗值。由於CRC32產生校驗值時源數據塊的每一個bit(位)都參與了計算,所以數據塊中即使只有1bit的數據發生了變化,也會得到不同的CRC32值.
什么是ACK?
ACK (Acknowledge character)即是確認字符,在數據通信中,接收站發給發送站的一種傳輸類控制字符。表示發來的數據已確認接收無誤。
block沿着數據流管道傳輸的好處在哪?為什么不將塊的數據分3個方向分別同時傳輸到不同的datanode節點?
因為沿着數據流管道傳輸可以將傳數據的壓力分配到不同的datanode節點,減少客戶端負荷,效率更高。
校驗失敗后,package重寫放到data queue的末尾,並不會打亂文件的寫入,每個package都有一個seqno序號,對號入座即可。
容錯機制(重新傳輸機制)

在上面的詳細過程中,我們說,如果數據傳輸出錯的話要重新傳輸,那重新傳輸的機制是怎樣的?比如說,一個package通過一個數據流管道:datanode1--->datanode2--->datanode3來進行傳輸,如果datanode1和datanode3數據都沒問題,就只有datanode2的數據傳輸有問題(如dn2掛了或者通信不暢了等問題),要怎么解決?
- 因為datanode2的package有問題,導致校驗結果失敗,客戶端會將ack queue隊列中的所有package放回到data queue末尾,准備重新傳輸該package
- 放回到data queue末尾后,客戶端會RPC遠程調用namenode的updataBlockForPipeline()方法,從而為當前block生成新的版本(實際上就是生成新的時間戳)。會從pipeline管道中刪除datanode2。
- 客戶端再RPC調用namenode的getAdditionalDatanode()方法,namenode返回給客戶端一個新的datanode,比如說datanode4。
- 輸出流將datanode1、datanode3、datanode4形成新的pipeline管道,並更新datanode1和datanode2中的block版本,把DN1或者DN3中更新后的block復制到DN4中,管道就重新建立好了。
- DFSOutputStream遠程調用namenode的updatePipeline()方法更新元數據。
- 開始沿着新管道重新傳輸未完成的package。
datanode2的故障排除並重啟后,會通過心跳與namenode進行通信,namenode發現datanode2上有些block的間戳是老的,datanode2就會將對應的這些block刪除。
🌈hdfs讀取流程(面試重點)
大致過程

1)客戶端通過Distributed FileSystem向NameNode請求下載文件,NameNode通過查詢元數據,找到文件塊所在的DataNode地址。
2)挑選一台DataNode(就近原則,然后隨機)服務器,請求讀取數據。
3)DataNode開始傳輸數據給客戶端(從磁盤里面讀取數據輸入流,以Packet為單位來做校驗)。
4)客戶端以Packet為單位接收,先在本地緩存,然后寫入目標文件。
注意:讀取一個文件不能並發讀寫(不能一次性讀多個該文件的block),但是可以一次性讀多個文件。
補充:如何判斷客戶端距離文件的某個block存放的datanode最近是誰?
從上圖可以看到,文件的某個塊的三個副本存放在了兩個不同的機架上,其中有一個block的datanode節點服務器和客戶端所在服務器是在同一台機架上的,很明顯block1離客戶端最近,因為如果讀取block2和block3需要經過層層交換機。
容錯機制
- 情況1,假如客戶端從datanode1下載文件過程中,dn1掛掉了,客戶端就會告知namenode:dn1掛掉了,然后客戶端嘗試從文件的block存放的另一個datanode節點下載數據。
- 情況2,假如客戶端從datanode1下載文件過程中,dn1出現了位衰減的問題,客戶端收到package后也是需要進行校驗,若校驗不通過,也會告知namenode:dn1有問題了,然后客戶端嘗試從文件的block存放的另一個datanode節點下載數據。
- 情況3,package校驗不通過不一定是位衰減等問題,有可能是網絡傳輸的問題。因此,package校驗不通過不會直接換節點傳輸,而是首先進行重試(重新傳輸),如果校驗結果再次不通過,客戶端才會告訴namenode,節點出問題了。

