當前位置:
首頁 > 最新 > Hadoop之HDFS

Hadoop之HDFS

1、初識HDFS

HDFS作為一個分散式文件系統,具有高容錯的特點,它可以部署在廉價的通用硬體上,提供高吞吐率的數據訪問,適合那些需要處理海量數據集的應用程序。HDFS沒有遵循可移植操作系統介面(Portable Operation SystemInterface,POSIX)的要求,不支持「ls」或「cp」這樣的標準UNIX命令,也不支持如fopen()和fread()這樣的文件讀寫方法,而是提供了一套特有的、基於Hadoop抽象文件系統的API,支持以流的形式訪問文件系統中的數據。

AHDFS的主要特性

HDFS的主要特性包括

支持超大文件。超大文件在這裡指的是幾百MB、幾百GB甚至幾TB大小的文件,一般來說,一個Hadoop文件系統會存儲T(1TB=1024GB)、P(1P=1024T)級別的數據。Hadoop需要能夠支持這種級別的大文件;

檢測和快速應對硬體故障。在大量通用硬體平台上構建集群時,故障,特別是硬體故障是常見的問題。一般的HDFS系統是由數百台甚至上千台存儲著數據文件的伺服器組成,這麼多的伺服器意味著高故障率。因此,故障檢測和自動恢復是HDFS的一個設計目標;

流式數據訪問。HDFS處理的數據規模都比較大,應用一次需要訪問大量的數據。同時,這些應用一般是批量處理,而不是用戶互動式處理。HDFS使應用程序能夠以流的形式訪問數據集,注重的是數據的吞吐量,而不是數據訪問的速度;

簡化的一致性模型。大部分的HDFS程序操作文件時需要一次寫入,多次讀取,在HDFS中,一個文件一旦經過創建、寫入、關閉後,一般就不需要修改了。這樣簡單的一致性模型,有利於提供高吞吐量的數據訪問模型。

正是由於以上的設計目標,HDFS並不適合如下應用:

低延遲數據訪問。低延遲數據,如和用戶進行交互的應用,需要數據在毫秒或秒的範圍內得到響應。由於Hadoop針對高數據吞吐量做了優化,而犧牲了獲取數據的延遲,對於低延遲訪問,可以考慮使用HBase或Cassandra;

大量的小文件。HDFS支持超大文件,是通過將數據分布在數據節點(DataNode),並將文件的元數據保存在名稱節點(NameNode)上。名稱節點的內存大小,決定了HDFS文件系統可保存的文件數量,雖然現在的系統內存都比較大,但大量的小文件還是會影響名稱節點的性能;

多用戶寫入文件、修改文件。HDFS中的文件只能有一個寫入者,而且寫操作總是在文件末尾。它不支持多個寫入者,也不支持在數據寫入後,在文件的任意位置進行修改。

總之,HDFS是為以流式數據訪問模式存儲超大文件而設計的文件系統,並在普通商用硬體集群上運行。

BHDFS的體系結構

為了支持流式數據訪問和存儲超大文件,HDFS引入了一些比較特殊的設計,在一個全分布模式環境的集群上,「運行HDFS」意味著在網路分布的不同伺服器上運行一些守護進程(daemon),這些進程有各自的特殊角色,並相互配合,一起形成一個分散式文件系統。

HDFS採用了主從(Master/Slave)式體系結構,名稱節點NameNode、數據節點DataNode和客戶端Client是HDFS中3個重要的角色。

在一個HDFS中,有一個名稱節點和一個第二名稱節點,典型的集群有幾十到幾百個數據節點,規模大的系統可達上千、甚至幾千個數據節點;而客戶端,一般情況下,比數據節點的個數還多。名稱節點和第二名稱節點、數據節點和客戶端的關係如下圖所示:

名稱節點可以看作是分散式文件系統中的管理者,它負責管理文件系統命名空間、集群配置和數據塊複製等。

數據節點是文件存儲的基本單元,它以數據塊的形式保存了HDFS中文件的內容和數據塊的數據校驗信息。

客戶端和名稱節點、數據節點通信,訪問HDFS文件系統,操作文件。

2、數據塊

在介紹上述各實體之前,首先了解一下HDFS中的重要概念:數據塊(Block)。

在討論文件系統的時候,特別是在分析文件系統的實現時,我們知道,為了便於管理,設備往往將存儲空間組織成為具有一定結構的存儲單位。如磁碟,文件是以塊的形式存儲在磁碟中,塊的大小代表系統讀/寫操作的最小單位;在Linux的Ext3文件系統中,塊大小默認為4096位元組。文件系統通過一個塊大小的整數倍的數據塊,來使用磁碟。磁碟上的數據塊管理屬於文件系統實現的內部細節,對於通過系統調用讀寫文件的用戶來說,是透明的。

HDFS也有塊的概念,不過是更大的單元,默認HDFS數據塊大小是64MB(1.x版本)/128MB(2.x版本)。和普通文件系統類似,HDFS上的文件也進行分塊,塊作為單獨的存儲單元,以Linux上普通文件的形式保存在數據節點的文件系統中。數據塊是HDFS的文件存儲處理的單元。

HDFS是針對大文件設計的分散式系統,使用數據塊帶來了很多的好處,具體如下:

HDFS可以保存比存儲節點單一磁碟大的文件。

文件塊可以保存在不同的磁碟上。其實,在HDFS中,文件數據可以存放在集群上的任何一個磁碟上,不需要保存在同一個磁碟上,或同一個機器的不同磁碟上。

簡化了存儲子系統。

簡單化是所有系統的追求,特別是在故障種類繁多的分散式系統中,將管理「塊」和管理「文件」的功能區分開,簡化了存儲管理,也消除了分散式管理文件元數據的複雜性。

方便容錯,有利於數據複製。

在HDFS中,為了應對損壞的塊以及磁碟、機器故障,數據塊會在不同的機器上進行複製(一般副本數為3,即一份數據保存在3個不同的地方),如果一個數據塊副本丟失或者損壞了,系統會在其他地方讀取副本,這個過程對用戶來說是透明的,它實現了分散式系統中的位置透明性和故障透明性。同時,一個因損壞或機器故障而丟失的塊會從其他地方複製到某一個正常運行的機器,以保證副本數目恢復到正常水平。該正常水平的副本數,也稱副本係數。

HDFS數據塊比前面討論過的磁碟塊大得多,一個典型的HDFS系統中,磁碟塊的大小為64MB,也有使用128MB和256MB數據塊大小的集群。為什麼在HDFS中要使用這麼大的數據塊呢?原因和在磁碟上使用大磁碟塊的原理是一樣的。在普通文件系統中使用較大的磁碟塊,可以減少管理數據塊需要的開銷,如在Linux中可以減少保存在i-node中磁碟地址表中的信息鏈的長度;同時,在對文件進行讀寫時,可以減少定址開銷,即磁碟定位數據塊的次數。HDFS中使用大數據塊,可以減少名稱節點上管理文件和數據塊關係的開銷,同時,對數據塊進行讀寫時,可以有效地減少建立網路連接需要的成本。

3、名稱節點和第二名稱節點

名稱節點(NameNode)是HDFS主從結構中主節點上運行的主要進程,它指導主從結構中的從節點——數據節點(DataNode)執行底層的I/O任務。

名稱節點是HDFS的書記員,維護著整個文件系統的文件目錄樹,文件/目錄的元信息和文件的數據塊索引,即每個文件對應的數據塊列表。這些信息以兩種形式存儲在本地文件系統中:一種是命名空間鏡像(FileSystem Image,FSImage,也稱文件系統鏡像),另一種是命名空間鏡像的編輯日誌(EditLog)(體現了HDFS最新的狀態)。

命名空間鏡像保存著某一特定時刻HDFS的目錄樹、元信息和數據塊索引等信息,後續對這些信息的改動,則保存在編輯日誌中,它們一起提供了一個完整的名稱節點第一關係。

同時,通過名稱節點,客戶端還可以了解到數據塊所在的數據節點信息。需要注意的是,名稱節點中與數據節點相關的信息不保留在名稱節點的本地文件系統中,也就是上面提到的命名空間鏡像和編輯日誌中,名稱節點每次啟動時,都會動態地重建這些信息,這些信息構成了名稱節點第二關係。運行時,客戶端通過名稱節點獲取上述信息,然後和數據節點進行交互,讀寫文件數據。

另外,名稱節點還能獲取HDFS整體運行狀態的一些信息,如系統的可用空間、已經使用的空間、各數據節點的當前狀態等。

第二名稱節點(Secondary NameNode,SNN)是用於定期合併命名空間鏡像和鏡像編輯日誌的輔助守護進程。和名稱節點一樣,每個集群都有一個第二名稱節點,在大規模部署的條件下,一般第二名稱節點也獨自佔用一台伺服器。

如前面所述,名稱節點是HDFS集群中的單一故障點,通過第二名稱節點的檢查點,可以減少停機的時間並降低名稱節點元數據丟失的風險。但是,第二名稱節點不支持名稱節點的故障自動恢復,名稱節點失效處理需要人工干預。

實踐

HDFS最新的操作日誌都保存在文件edits_inprogress文件中,並且這些edits文件都是二進位形式,HDFS提供了一個命令工具——edits viewer日誌查看器,專門用於查看edits文件的內容。使用如下命令在HDFS文件系統中創建一個示例目錄:

hdfs dfs -mkdir /myexample

運行命令find . -name edits*,找到保存edits文件的目錄,並進入到該目錄下。運行如下命令將最新操作日誌文件,導出到xml文件中,以查看其內容:

hdfs oev -i edits_inprogress_0000000000000000110 -o ~/example1.xml

cat ~/example1.xml

結果如下圖所示:

HDFS中數據文件的元信息(數據塊的存儲位置及冗餘信息等)都保存在fsimage文件中,它們同樣也是二進位形式,並且跟edits文件保存在同一個目錄下。HDFS也提供了一個命令工具——image viewer專門用於查看fsimage文件的內容,需要注意的是,在使用該工具時,要使用-p參數指定輸出文件的類型,如下:

hdfs oiv -i fsimage_0000000000000000002 -o ~/example2.xml -p XML

cat ~/example2.xml

輸出結果部分內容截圖如下所示:

4、數據節點

HDFS集群上的從節點都會駐留一個數據節點的守護進程,來執行分散式文件系統中最忙碌的部分:將HDFS數據塊寫到Linux本地文件系統的實際文件中,或者從這些實際文件中讀取數據塊。

雖然HDFS是為大文件設計,但放在HDFS上的文件和傳統文件系統類似,也是將文件分塊,然後進行存儲。但和傳統文件系統不同,在數據節點上,HDFS文件塊(也就是數據塊)以Linux文件系統上的普通文件進行保存。客戶端進行文件內容操作時,先由名稱節點告知客戶端每個數據塊駐留在哪個數據節點,然後客戶端直接與數據節點守護進程進行通信,處理與數據塊對應的本地文件。同時,數據節點會和其他數據節點進行通信,複製數據塊,保證數據的冗餘性(注意:數據塊的冗餘度一般跟數據節點的個數保持一致,最大不要超過3個,在生產環境中,至少保持兩個數據節點)。

數據節點作為從節點,會不斷地向名稱節點報告。初始化時,每個數據節點將當前存儲的數據塊告知名稱節點。後續數據節點工作過程中,數據節點仍會不斷地更新名稱節點,為之提供本地修改的相關信息,並接受來自名稱節點的指令,創建、移動或者刪除本地磁碟上的數據塊。下圖說明了名稱節點和數據節點的角色:

上圖中顯示兩個數據文件,它們都位於」/data」目錄下。其中,「data1.txt」文件有3個數據塊,表示為b1、b2和b3,「data2.txt」文件由數據塊b4和b5組成。這兩個文件的內容分散在幾個數據節點上。示例中,每個數據塊都有3個副本。如數據塊1(屬於」data1.txt」文件)的3個副本分布於DataNode1、DataNode2和DataNode3上,當這些數據節點中任意一個崩潰或者無法通過網路訪問時,可以通過其他節點訪問「data1.txt」文件。

實踐

上傳一個大小超過128MB的文件到HDFS中,查看數據塊文件的情況。運行如下命令上傳hadoop-2.7.3.tar.gz文件到HDFS的/tmp目錄下:

hdfs dfs -put /root/tools/hadoop-2.7.3.tar.gz /tmp

運行命令find . -name blk*,找到保存數據塊文件的目錄,並進入該目錄,運行命令:ls –l,結果如下圖所示:

從圖中可以看到,blk_1073741838文件和blk_1073741839文件便是hadoop-2.7.3.tar.gz對應的數據塊文件,這兩個文件的大小之和便是hadoop-2.7.3.tar.gz文件的實際大小。

5、客戶端

客戶端是用戶和HDFS進行交互的手段,HDFS提供了各種各樣的客戶端,包括命令行介面、Java API、Thrift介面、C語言庫、用戶空間文件系統(Filesystem in Userspace,FUSE)等。

雖然Hadoop不是一個POSIX文件系統,不支持「ls」和「cp」這樣的命令,但Hadoop提供了一套和Linux文件命令類似的命令行工具,使熟悉Linux文件系統的用戶,可以很快使用該工具,對HDFS進行操作。例如,下面的命令可以在HDFS文件系統上創建一個目錄:

hadoop fs –mkdir testDIR

通過命令行工具,可以進行一些典型的文件操作,如讀文件、創建文件路徑、移動文件(包括文件改名)、刪除文件、列出文件列表等,同時,命令行工具也提供了本地文件和HDFS交互的能力,可以通過下面的命令,將本地文件上傳到HDFS:

hdfs dfs –copyFromLocal testInput/hello.txt/user/alick/in/hello.txt

命令行工具提供了訪問HDFS的基本能力,HDFS的Java API提供了更進一步的功能,目前,所有訪問HDFS的介面都是基於Java API,包括上面介紹的命令行工具。HDFS的Java API實現了上一章介紹的Hadoop抽象文件系統,包括Distributed File System和對應的輸入/輸出流。

下面是一個非常簡單的HDFS JavaAPI的例子

Configurationconf=newConfiguration();

conf.set("fs.defaultFS","hdfs://192.168.12.111:9000");

FileSystemclient= FileSystem.get(conf);

OutputStreamos = client.create(new Path("/tools/a.zip"));

FileStatus[]status=client.listStatus(newPath("/tools"));

for(FileStatuss:status) {

}

client.delete(newPath("/tools/b.txt"));

client.close();

在上面的代碼中,通過FileSystem.get()獲取的文件系統是DistributedFileSystem實例,而通過FileSystem.create()創建的輸出流,則是一個FSDataOutputStream實例。方法listStatus()和delete()用於獲得HDFS上文件狀態信息和刪除文件,它們都是標準的Hadoop文件系統方法。

HDFS正是通過Java的客戶端,屏蔽了訪問HDFS的各種各樣細節,用戶通過標準的Hadoop文件介面,就可以訪問複雜的HDFS,而不需要考慮與名稱節點、數據節點等的交互細節,降低了Hadoop應用開發的難度,也證明了Hadoop抽象文件系統的適用性。

此外,HDFS的Thrift介面、C語言庫和FUSE等模塊,它們和HDFS的命令行一樣,都是在JavaAPI的基礎上開發的,用於訪問HDFS的介面,這裡不再詳細介紹。

6HDFS主要操作流程

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

A、客戶端到名稱節點的文件與目錄操作

客戶端有到名稱節點的大量元數據操作,如更改文件名(rename)、在給定目錄下創建一個子目錄(mkdir)等,這些操作一般只涉及客戶端和名稱節點的交互,通過遠程介面ClientProtocol進行。

以創建子目錄為例,如下圖所示:

當客戶端調用HDFS的FileSystem實例,也就是DistributedFileSystem的mkdir()方法時(如圖中步驟1),DistributedFileSystem對象通過IPC調用名稱節點上的遠程方法mkdir(),讓名稱節點執行具體的創建子目錄操作:在目錄樹數據結構上的對應位置創建新的目錄節點,同時記錄這個操作並持久化到日誌中,方法執行成功後,mkdir()返回true,結束創建過程。期間,客戶端和名稱節點都不需要和數據節點交互。

一些更為複雜的操作,如使用DistributedFileSystem.setReplication()增加文件的副本數,再如通過DistributedFileSystem.delete()刪除HDFS上的文件,都需要數據節點配合執行一些動作。

以客戶端刪除HDFS文件為例(如下圖所示),操作在名稱節點上執行完畢後,數據節點上存放文件內容的數據塊也必須刪除。但是,名稱節點執行delete()方法時,它只標記操作涉及的需要被刪除的數據塊(當然,也會記錄delete操作並持久化到日誌),而不會主動聯繫保存這些數據塊的數據節點,立即刪除數據。當保存著這些數據塊的數據節點向名稱節點發送心跳時(如圖中的步驟3),在心跳的應答里,名稱節點會通過DatanodeCommand命令數據節點刪除數據。在這個過程中,需要注意兩個要點:被刪除文件的數據,也就是該文件對應的數據塊,在刪除操作完成後的一段時間以後,才會被真正刪除;名稱節點和數據節點間永遠維持著簡單的主從關係,名稱節點不會向數據節點發起任何IPC調用,數據節點需要配合名稱節點執行的操作,都是通過數據節點心跳應答中攜帶的DatanodeCommand返回。

B、客戶端讀文件

下圖顯示了在讀取HDFS上的文件時,客戶端、名稱節點和數據節點間發生的一些事件以及事件的順序。

客戶端通過FileSystem.open()打開文件,對應的HDFS具體文件系統DistributedFileSystem創建輸入流FSDataInputStream,返回給客戶端,客戶端使用這個輸入流讀取數據。FSDataInputStream需要和具體的輸入流結合,一起形成過濾器流(filtered stream)向外提供服務。

對HDFS來說,具體的輸入流是DFSInputStream。在DFSInputStream的構造函數中,輸入流實例通過ClientProtocal.getBlockLocation()遠程介面調用名稱節點,以確定文件開始部分數據塊的保存位置,即圖中的步驟2。對於文件中的每個塊,名稱節點返回保存著該塊副本的數據節點地址。注意,這些數據節點根據它們與客戶端的距離(利用了網路的拓撲信息),進行了簡單的排序。

客戶端調用FSDataInputStream.read()方法讀取文件數據時,DFSInputStream對象會通過和數據節點間的「讀數據」流介面,和最近的數據節點建立聯繫。客戶端反覆調用read()方法,數據會通過數據節點和客戶端連接上的數據包返回客戶端。當到達塊的末端時,DFSInputStream會關閉和數據節點間的連接,並通過getBlockLocation()遠程方法獲得保存著下一個數據塊的數據節點信息(嚴格說,在對象沒有緩存該數據塊的位置時,才會使用這個遠程方法),即圖中的步驟5,然後繼續尋找最佳數據節點,再次通過數據節點的讀數據介面,獲得數據(圖中的步驟6).

另外,由於ClientProtocol.getBlockLocations()不會一次返迴文件的所有數據塊信息,DFSInputStream可能需要多次使用該遠程方法,檢索下一組數據塊的位置信息。對於客戶端來說,它讀取的是一個連續的數據流,上面分析的聯繫不同數據節點、定位一組數據塊位置的過程,對它來說都是透明的。當客戶端完成數據讀取任務後,通過FSDataInputStream.close()關閉輸入流(圖中的步驟7).

在客戶端讀取文件時,如果數據節點發生了錯誤,如節點停機或者網路出現故障,那麼客戶端會嘗試下一個數據塊位置。同時,它也會記住出現故障的那個數據節點,不會再進行徒勞無益的嘗試。讀數據的應答包中,不但包含了數據,還包含了數據的校驗和,客戶端會檢查數據的一致性,如果發現有校驗錯誤,也就是說數據塊已經損壞,它會將這個信息報告給名稱節點,同時,嘗試從別的數據節點中讀取另外一個副本的文件內容。由客戶端在讀數據時進行數據完整性檢查,可以降低數據節點的負載,均衡各節點的計算能力。

由客戶端直接聯繫名稱節點,檢索數據存放位置,並由名稱節點安排數據節點讀取順序,這樣的設計還有一個好處是,能夠將讀取文件引起的數據傳輸,分散到集群的各個數據節點,HDFS可以支持大量的並發客戶端。同時,名稱節點只處理數據塊定位請求,不提供數據,否則,隨著客戶端數量的增長,名稱節點會迅速成為系統的「瓶頸」。

C、客戶端寫文件

即使不考慮數據節點出錯後的故障處理,文件寫入也是HDFS中最複雜的流程。以創建一個新文件並向文件中寫入數據,然後關閉文件為例,分析客戶端寫文件時系統各節點的配合,如下圖所示:

客戶端調用DistributedFileSystem的create()方法創建文件(如圖中的步驟1),這時,DistributedFileSystem創建DFSOutputStream,並由遠程過程調用,讓名稱節點執行同名方法,在文件系統的命名空間中創建一個新文件。名稱節點創建新文件時,需要執行各種各樣的檢查,如名稱節點處於正常工作狀態,被創建的文件不存在,客戶端有在父目錄中創建文件的許可權等。這些檢查都通過以後,名稱節點會構造一個新文件,並記錄創建操作到編輯日誌edits中。遠程方法調用結束後,DistributedFileSystem將該DFSOutputStream對象包裹在FSDataOutputStream實例中,返回給客戶端。

在上圖中的步驟3客戶端寫入數據時,由於create()調用創建了一個空文件,所以,DFSOutputStream實例首先需要向名稱節點申請數據塊,addBlock()方法成功執行後,返回一個LocatedBlock對象。該對象包含了新數據塊標識和版本號,同時,它的成員變數LocatedBlock.locs提供了數據流管道的信息,通過上述信息,DFSOutputStream就可以和數據節點聯繫,通過寫數據介面建立數據流管道。客戶端寫入FSDataOutputStream流中的數據,被分成一個一個的文件包,放入DFSOutputStream對象的內部隊列。該隊列中的文件包最後打包成數據包,發往數據流管道,流經管道上的各個數據節點,並持久化。確認包(如圖中的步驟6)逆流而上,從數據流管道一次發往客戶端,當客戶端收到應答時,它將對應的包從內部隊列移除。

DFSOutputStream在寫完一個數據塊後,數據流管道上的節點,會通過和名稱節點的DatanodeProtocol遠程介面的blockReceived()方法,向名稱節點提交數據塊。如果數據隊列中還有等待輸出的數據,DFSOutputStream對象需要再次調用addBlock()方法,為文件添加新的數據塊。

客戶端完成數據的寫入後,調用close()方法關閉流,如圖中的步驟8。關閉意味著客戶端不會再往流中寫入數據,所以,當DFSOutputStream數據隊列中的文件包都收到應答後,就可以使用ClientProtocol.complete()方法通知名稱節點關閉文件,完成一次正常的寫文件流程。

如果在文件數據寫入期間,數據節點發生故障,則會執行下面的操作(注意,這些操作對於寫入數據的客戶端而言是透明的):首先,數據流管道會被關閉,已經發送到管道但還沒有收到確認的文件包,會重新添加到DFSOutputStream的輸出隊列,這樣保證了無論數據流管道中哪個數據節點故障,都不會丟失數據。當前正常工作的數據節點上的數據塊會被賦予一個新的版本號,並通知名稱節點,這樣,失敗的數據節點從故障恢復過來以後,上面只有部分數據的數據塊會因為數據塊版本號和名稱節點保存的版本號不匹配而刪除。然後,在數據流管道中刪除錯誤數據節點並重新建立管道,並繼續寫數據到正常工作的數據節點。文件關閉後,名稱節點會發現該數據塊的副本數沒有得到要求,會選擇一個新的數據節點並複製數據塊,創建新的副本。數據節點故障只會影響一個數據塊的寫操作,後續數據塊寫入不會受到影響。

在數據塊寫入過程中,可能出現多於一個的數據節點出現故障的情況,這時,只要數據流管道中的數據節點滿足配置項$的值(默認值是1),就認為寫操作是成功的。後續這個數據塊會被複制,直到滿足文件的副本係數的要求。

D、數據節點的啟動和心跳

這裡討論數據節點和名稱節點間的交互,如下圖所示。其中包括數據節點從啟動到進入正常工作狀態的註冊,數據塊上報,以及正常工作過程中的心跳等與名稱節點相關的遠程調用。這部分雖然只涉及DatanodeProtocol介面,但有助於讀者更進一步理解數據節點和名稱節點的關係。

正常啟動數據節點或者為升級而啟動數據節點,都會向名稱節點發送遠程調用versionRequest(),進行必要的版本檢查。這裡的版本檢查,只涉及構建版本號,保證它們間的HDFS版本是一致的。正常啟動的數據節點,在版本檢查結束後,在圖中的步驟2會接著發送遠程調用register(),向名稱節點註冊。DatanodeProtocol.register()的主要工作也是檢查,通過檢查確認該數據節點是名稱節點管理集群的成員。也就是說,用戶不能將某一個集群的數據節點,直接註冊到另一個集群的名稱節點,這保證了整個系統的數據一致性。註冊成功後,數據節點會將它管理的所有數據塊信息,通過blockReport()方法上報到名稱節點,幫助名稱節點建立HDFS文件數據塊到數據節點的映射關係。這一步操作完成後,數據節點才正式提供服務。(數據節點和名稱節點間的交互)

由於名稱節點和數據節點間存在著主從關係,數據節點需要每隔一段時間發送心跳到名稱節點(如圖中的步驟4、步驟5),如果名稱節點長時間接收不到數據節點的心跳,它會認為該數據節點已經失效。名稱節點如果有一些需要數據節點配合的動作,則會通過方法sendHeartBeat()返回。該返回值是一個DatanodeCommand數組,它帶回來一系列名稱節點指令。繼續上面提到的客戶端刪除HDFS文件例子,操作在名稱節點上執行完畢後,被刪除文件的數據塊會被標記,如果保存這些數據塊的數據節點向名稱節點發送心跳,則在返回的DatanodeCommand數組裡,有對應的命令編號為DNA_INVALIDATE的名稱節點指令。數據節點執行指令,刪除數據塊,釋放存儲空間。注意,數據節點刪除數據塊時,並不知道、也不需要知道該數據塊是因為文件被刪除,還是均衡器移動數據塊後導致副本數升高,或者是用戶降低文件的副本係數觸發的數據塊刪除,它只需要簡單地執行名稱節點指令即可。

應該說,數據節點和名稱節點間的交互非常簡單,大部分都是數據節點到名稱節點的心跳,但考慮到在一個大規模的HDFS集群中,一個名稱節點會管理上千個數據節點,這樣的設計也就非常自然。

E、第二名稱節點合併元數據

客戶端對HDFS的文件系統目錄樹進行修改時,名稱節點都會在編輯日誌里寫下記錄,以保證系統出現故障後,能夠根據這些日誌進行恢復。日誌會隨時間不斷地增長,意味著如果系統重啟後,需要進行日誌恢復的時間會很長。為了避免這種情況發生,HDFS引入了檢查點機制,命名空間鏡像(fsimage)文件就是文件系統元數據的持久性檢查點,和編輯日誌不同,它不能在每次對文件系統元數據進行修改後都進行更新。在一個比較大的運營集群中,fsimage文件可以有GB的大小,所以命名空間鏡像加上編輯日誌,為名稱節點的元數據提供了安全保障。如果名稱節點重新啟動,元數據可以通過從磁碟中讀入命名空間鏡像,恢復到某一個檢查點,然後再執行檢查點後記錄的編輯日誌,進行重建。一般來說,fsimage中包括了序列化後的文件系統目錄和文件的i-node。和Linux的i-node相似,HDFS索引節點表徵一個文件或者目錄的元數據信息,以及文件的副本數、修改和訪問時間等信息。

上述機制有一個問題,Hadoop文件系統的編輯日誌會隨時間不斷增長,如果HDFS重新啟動,名稱節點將花很長時間執行編輯日誌中的每一個操作,在這個時間內,文件系統是不可用的,減少恢復時間,有利於提供系統的可用性。為了解決這個問題,Hadoop引入了第二名稱節點,它的唯一工作就是輔助名稱節點,合併fsimage和編輯日誌。第二名稱節點合併元數據的具體流程如下圖所示。過程涉及遠程介面NamenodeProtocol和名稱節點、第二名稱節點上的非IPC介面。

該過程由第二名稱節點發起,首先通過遠程方法NamenodeProtocol.getEditLogSize()獲得名稱節點上編輯日誌的大小。如果日誌很小,第二名稱節點就不需要合併元數據鏡像和編輯日誌,否則,繼續通過該遠程介面上的rollEditLog(),啟動一次檢查點過程。這時,名稱節點需要創建一個新的編輯日誌edit.inprogress,後續對文件系統元數據的改動,都會記錄到這個新的日誌里。而原有的命名空間鏡像和編輯日誌,則由第二名稱節點,通過HTTP介面(如圖中的步驟3和步驟4),讀取到本地,並在內存中進行合併。合併的結果輸出為fsimage.check,然後,第二名稱節點再次發起HTTP請求,通知名稱節點數據已經準備好。名稱節點通過圖中步驟6的HTTP GET請求,下載fsimage.check。名稱節點下載新命名空間鏡像結束後,第二名稱節點通過NamenodeProtocol.rollFsImage(),完成這次檢查點。名稱節點在處理該遠程方法時,會用fsimage.check覆蓋原來的fsimage,形成新的命名空間鏡像,同時將新的編輯日誌edit.inprogress改名為edit,注意,該編輯日誌中的內容包含了在新元數據鏡像的基礎上,對文件系統的修改。

7、小結

作為支撐海量數據處理的文件系統,HDFS需要提供與普通文件系統不一樣的特性,這是支持超大文件、檢測和快速恢復硬體故障、流式數據訪問和簡化的一致性模型這些設計目標,決定了HDFS採用了主從式體系結構,系統的功能分布在名稱節點和第二名稱節點、數據節點和客戶端等實體中,它們相互配合,為海量數據處理提供底層支持。

介面是觀察複雜系統工作的一個很好的出發點,HDFS是採用多項分散式技術實現的文件系統,HDFS各個實體間存在著多種信息交互的過程,這些交互過程,有些使用Hadoop遠程過程調用實現,充分利用了IPC機制特有的,和本地過程調用相同的語法外觀,在需要交換大量數據的場景中,則使用基於TCP或者基於HTTP的流式介面。從另一個角度看,這些介面有些和客戶端相關,有些是HDFS各個伺服器間,也就是HDFS內部實體間的交互。HDFS各個實體間的介面如下圖所示:

基於IPC的,和客戶端相關的介面包括ClientProtocol、ClientDatanodeProtocol,分別用於客戶端和名稱節點、客戶端和數據節點的通信。HDFS內部實體使用Hadoop遠程過程調用實現的信息交換包括數據節點和名稱節點、數據節點和數據節點、第二名稱節點和名稱節點間的交互。流式介面主要用於大數據傳輸,包括了客戶端和數據節點、數據節點和數據節點間的基於TCP的,與數據塊操作相關的介面,以及用於第二名稱節點合併命名空間鏡像和鏡像編輯日誌,基於HTTP協議的介面。

參考文獻:

——《潭州大數據課程課件》

——《Hadoop技術內幕 深入解析HADOOP COMMON和HDFS架構設計與實現原理》


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序猿的修身養性 的精彩文章:

Hadoop環境安裝篇
Linux操作系統篇

TAG:程序猿的修身養性 |