Hadoop源碼分析筆記(八):HDFS主要流程

HDFS主要流程

        本節介紹5個典型的HDFS流程:客戶端到名字節點的元數據操作、客戶端讀文件、客戶端寫文件、數據節點到名字節點的註冊和心跳、以及第二名字節點的合併數據。這些流程充分體現了HDFS實體間IPC接口和流式接口的配合。

客戶端到名字節點的文件與目錄操作

        客戶端有到名字節點的大量元數據的操作,如更改文件名、在給定目錄下創建一個子目錄等,這些操作一般只涉及客戶端和名字節點的交互,通過遠程接口ClientProtocol進行。
         以創建子目錄爲例:
         當客戶端調用HDFS的FileSystem實例,也就是DistributedFileSysstem的mkdir()方法時,讓名字節點執行具體的創建子目錄操作:在目錄樹數據結構上對應位置創建新的目錄節點,同時記錄這個操作並持久化到日誌中,方法執行後,mkdir()方法返回ture,結束創建過程。期間,客戶端和名字節點都不需要和數據節點交互。
        以客戶端刪除HDFS文件爲例:
        操作在名字節點執行完畢後,數據節點上存放文件的內容的數據塊也必須刪除。但是,名字節點執行delete()方法時,它只標記操作涉及的需要被刪除的數據塊(當然也會記錄delete操作並持久化到日誌),而不會主動聯繫保存這些數據塊的數據節點,立即刪除數據。當保存着這些數據塊的數據節點向名字節點發送心跳時,在心跳的應答中名字節點會通過DatanodeCommon命令數據節點刪除數據塊,在刪除操作完成後的一段時間以後,纔會被真正刪除;名字節點和數據節點間永遠維持着簡單的主從關係,名字節點不會向數據節點發起任何IPC調用,數據節點需要配合名字節點執行的操作,都是通過數據節點心跳應答中攜帶的DatanodeCommand返回。

客戶端讀文件

        在客戶端讀取文件的過程中,客戶端通過FileSystem.open()打開文件,對應的HDFS具體的文件系統,DistributedFileSystem創建輸出流FSDataInoutStream,返回給客戶端,客戶端使用這個輸入流讀取數據。FSDataInputStream需呀和具體的輸入流結合,一起形成過濾器流向外提供服務。
        對於HDFS來說,具體的輸入流爲DFSInputStream。在DFSInputStream的構造函數中,輸出流通過ClientProtocol.getBlockLocations()遠程接口調用名字節點,以確定文件開始部分數據塊的保存位置,對於文件中的每個塊,名字節點返回保存着該副本的數據節點地址。當然,這些數據節點根據它們與客戶端的距離(利用網絡的拓撲信息),進行了簡單的排序。
        客戶端調用FSdataInputStrea.read()方法讀取文件時,DFSInputStream對象會通過和數據節點的“讀數據”流接口,和最近的數據節點建立聯繫。客戶端反覆調用read()方法,數據會通過數據節點和客戶端連接上的數據包返回客戶端。當達到塊的末端時,DFSInputStream會關閉和數據節點間的聯繫,並通過getBlockLications()遠程方法獲得保存着下一個數據塊的數據節點信息(嚴格說,在對象沒有緩存該數據塊的位置時,纔會使用這個遠程方法)。
        由客戶端直接聯繫名字節點,檢索數據存放位置,並由名字節點安排數據節點讀取順序,這樣的設計還有一個好處是,能夠將讀取文件引起的數據傳輸,分散到集羣的各個數據節點,HDFS可以支持大量的併發客戶端。同時,名字節點只處理數據塊定位請求,不提供數據,否則,隨着客戶端數量的增長,名字節點會迅速成爲系統的瓶頸。

客戶端寫文件

        即使不考慮數據節點出錯後的故障處理,文件寫入也是HDFS中最複雜的流程。
        以創建一個新文件並向文件寫入數據,然後關閉文件爲例:
        客戶端調用DistributedFileSystem的create()方法創建文件,這時,DistributedFileSystem創建DFSOuptStream,並由遠程過程調用,讓名字節點執行同名方法,在文件系統的命名空間中創建一個新文件。名字節點創建新文件時,需要執行各種各樣的檢查,並記錄穿件操作到編輯日誌edits中。遠程方法調用結束後,DistributedFileSystem將該DFSOutputStream對象包裹在FSDataOutputStream實例中,返回給客戶端。
        在客戶端寫入數據時,由於create()調用創建了一個空文件,所有DFSOutputStream實例首先需要向名字節點申請數據塊,addBlock()方法成功執行後,返回一個LocatedBlock對象。該對象包含了新數據塊的標識和版本號,同時LocatedBlock.locs提供了數據流管道的信息,通過上述信息,DFSOutputStream就可以和數據節點聯繫,通過寫數據接口建立數據流管道。客戶端寫入FSDataOutputStream流中數據,被分成一個一個文件包,放入DFSOutputStream對象內部列隊。該隊列中的文件包最後打包成數據包,發往數據流管道,並按照前面討論的方式,流經管道上的各個數據節點,並持久化。確定包逆流而上,從數據流管道依次發往客戶端,當客戶端收到應答時,它將對應的包從內部隊列移除。
        DFSOutputStream在寫完一個數據塊後,數據流管道上的節點,會通過和名字節點的DatanodeProtocol遠程接口的blockReceived()方法,向名字節點提交數據塊。如果數據隊列中還有等待輸出的數據,DFSOutputStream對象需要再次調用addBlock()方法,爲文件添加新的數據塊。
        客戶端完成數據的寫入後,調用close()方法關閉流。關閉意味着客戶端不會再往流中寫入數據,所有,當DFSOutputStream數據列隊中的文件包都收到應答後,就可以使用ClientProtocol.complete()方法通知名字節點關閉文件,完成一次正常的寫文件流程。
       如果在文件數據寫入期間,數據節點出現故障,如果文件包沒有收到應答,會重新添加到DFSOutputStream的輸出隊列。當前正常工作的數據節點上的數據塊會賦予一個新的版本號,並通知名字節點。當出現故障的數據節點從故障中恢復過來以後,這樣只有部分數據的數據塊會因爲數據塊版本號和名字節點保存的版本號不匹配而刪除。
      另外,在數據塊寫入過程中,可能會出現對於一個數據節點出現出現故障的情況下,這時,只要數據流管道中的數據節點數滿足配置項${dfs.replication.min}的值(默認值爲1),就認爲寫操作是成功的。後續這個數據塊會被複制,直到滿足文件的副本系數的要求。

數據節點的啓動和心跳

         正常啓動數據節點或者爲升級而啓動數據節點,都會向名字節點發送遠程調用versionRequest(),進行必要的版本檢查。這裏的版本檢查,只涉及構建版本號,保證他們間的HDFS版本是一致的。正常啓動的數據節點,在版本檢查結束後,會接着發送遠程調用register(),向名字節點註冊。DatanodeProtocol.register()的主要工作是檢查,通過檢查確認該數據節點是名字節點管理集羣的成員。也就是說,用戶不恩給你將某一個集羣的數據節點,直接註冊到另一個集羣的名字節點,這保證了整個系統的數據一致性。註冊成功後,數據節點會將他管理的所有數據塊信息,通過blockReport()方法上報到名字節點,幫助名字節點建立HDFS文件數據塊到數據節點的影射關係。這一步操作完成後,數據節點纔會正式提供服務。
        由於名字節點和數據節點間存在着主從關係,數據節點需要每隔一段時間發送心跳到名字節點,如果名字節點長時間接收不到數據節點的心跳,它會認爲該數據節點已經失效。名字節點如果有一些需要數據節點配合的動作,則會通過方法sendHeartbeat()返回。該返回值是一個DatanodeCommond數組,它帶來了一系列名字節點指令。繼續上面提到的客戶端刪除HDFS文件的例子,操作在名字節點執行完畢後,被刪除文件的數據塊會被標記,如果保存這些數據塊的數據節點向名字節點發送心跳,則在放回的DatanodeCommonde數組裏,有對應的命名編號爲DNA_INVALIDATE的名字節點指令。數據節點執行指令,刪除數據塊,釋放存儲空間。
        考慮到一個規模的HDFS集羣,一個名字節點會管理上千個數據節點,數據節點和名字節點的大部分交互都是數據節點到名字節點的心跳,這樣的設計是非常棒的。

第二名字節點合併元數據

        客戶端對HDFS的文件系統目錄樹進行修改時,名字節點都會在編輯日誌裏寫入記錄,以保證系統出現故障後,能夠根據這些日誌進行恢復。日誌會隨時間不斷地增長,意味着如果系統重啓後,需要進行日誌恢復的時間會很長。爲了避免這種情況的發生,HDFS引入了檢查點機制,命名空間鏡像(fsimage)文件就是系統元數據的持久性檢查點,和編輯日誌不同,它不能再每次對系統元數據進行修改後都進行更新。在一個比較大的運營集羣中,fsimage文件可以有GB的大小。如果名字節點重新啓動,元數據可以通過從磁盤中讀入命名空間鏡像,恢復到某一個恢復點,然後再執行檢查點後記錄的編輯日誌,進行重建。
        爲了解決Hadoop文件系統的編輯日誌不斷增長的問題,Hadoop引入了第二名字節點,它的唯一工作就是輔助名字節點,合併fsimage和編輯日誌。
       該過程由第二名字節點發起,首先通過調用遠程方法NamenodeProtocol.getEditLogSize()獲得名字節點上編輯日誌的大小。如果日誌很小,就不需要合併元數據鏡像和編輯日誌,否則繼續通過該遠程接口上的rollEditLog(),啓動一次檢查點過程。這時,名字節點需要創建一個新的編輯日誌edit.new,後續對文件系統元數據的改動,都會記錄到這個新的日誌裏。而原有的命名空間鏡像和編輯日誌,則由第二名字節點,通過前面分析的HTTP接口讀取到本地,並在內存中進行合併。合併後的結果輸出爲fsimage.ckpt,然後第二名字節點再次發起HTTP請求,通知名字節點數據已經準備好。名字節點通過HTTP GET請求,下載fsimage.ckpt。名字節點下載新的命名空間鏡像結束後,會用fsimage.ckpt覆蓋原來的fsimage,形成新的命名空間鏡像,同時將新的編輯日誌edit.new改名爲edit。

        

 版權申明:本文部分摘自【蔡斌、陳湘萍】所著【Hadoop技術內幕 深入解析Hadoop Common和HDFS架構設計與實現原理】一書,僅作爲學習筆記,用於技術交流,其商業版權由原作者保留,推薦大家購買圖書研究,轉載請保留原作者,謝謝!
發佈了1 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章