《Hadoop權威指南》第三章 Hadoop分布式文件系統
目錄
前言 HDFS的設計 HDFS的概念 命令行接口 Hadoop文件系統 Java接口 數據流 通過distcp并行復制
注:《Hadoop權威指南》重點學習摘要筆記
1. 前言
當數據集的大小超過一臺獨立的物理計算機的存儲能力時,就有必要對它進行分區(partition)并存儲到若干臺單獨的計算機上。管理網絡中跨多臺計算機存儲的文件系統稱為分布式文件系統(distributed filesystem)。該系統架構于網絡之上,勢必會引人網絡編程的復雜性,因此分布式文件系統比普通磁盤文件系統更為復雜。例如,使文件系統能夠容忍節點故障且不丟失任何數據,就是一個極大的挑戰。
Hadoop自帶一個稱為HDFS的分布式文件系統,即Hadoop Distributed Filesystem。在非正式文檔或舊文檔以及配置文件中,有時也簡稱為DFS,它們是一回事兒。HDFS是Hadoop的旗艦級文件系統,也是本章的重點,但實際上Hadoop是一個綜合性的文件系統抽象,因此接下來我們將了解將Hadoop與其他存儲系統集成的途徑,例如本地文件系統和AmazonS3系統。
2. HDFS的設計
HDFS以流式數據訪問模式來存儲超大文件,運行于商用硬件集群上 。讓我們仔細看看下面的描述:
超大文件:超大文件在這里指具有幾百MB,幾百GB甚至幾百TB大小的文件。目前已經有存儲PB級數據的Hadoop集群了。 流式數據訪問HDFS的構建思路是這樣的:一次寫人、多次讀取是最高效的訪問模式。數據集通常由數據源生成或從數據源復制而來,接著長時間在此數據集上進行各種分析。每次分析都將涉及該數據集的大部分數據甚至全部,因此讀取整個數據集的時間延遲比讀取第一條記錄的時間延遲更重要。 商用硬件Hadoop并不需要運行在昂貴且高可靠的硬件上。它是設計運行在商用硬件(在各種零售店都能買到的普通硬件)的集群上的,因此至少對于龐大的集群來說,節點故障的幾率還是非常高的。HDFS遇到上述故障時,被設計成能夠繼續運行且不讓用戶察覺到明顯的中斷。 同樣,那些不適合在HDFS上運行的應用也值得研究。目前HDFS對某些應用領域并不適合,不過以后可能會有所改進。
低時間延遲的數據訪問:要求低時間延遲數據訪問的應用,例如幾十毫秒范圍,不適合在HDFS上運行。記住,HDFS是為高數據吞吐量應用優化的,這可能會以提高時間延遲為代價。目前,對于低延遲的訪問需求,HBase是更好的選擇。 大量的小文件:由于namenode將文件系統的元數據存儲在內存中,因此該文件系統所能存儲的文件總數受限于namenode的內存容量。根據經驗,每個文件、目錄和數據塊的存儲信息大約占150字節。因此,舉例來說,如果有一百萬個文件,且每個文件占一個數據塊,那至少需要300MB的內存。盡管存儲上百萬個文件是可行的,但是存儲數十億個文件就超出了當前硬件的能力。 多用戶寫入,任意修改文件:HDFS中的文件寫人只支持單個寫人者,而且寫操作總是以“只添加”方式在文件末尾寫數據。它不支持多個寫人者的操作,也不支持在文件的任意位置進行修改。可能以后會支持這些操作。
3. HDFS的概念
1. 數據塊
每個磁盤都有默認的數據塊大小,這是磁盤進行數據讀/寫的最小單位。構建于單個磁盤之上的文件系統通過磁盤塊來管理該文件系統中的塊,該文件系統塊的大小可以是磁盤塊的整數倍。文件系統塊一般為幾千字節,而磁盤塊一般為512字節。這些信息(文件系統塊大小)對于需要讀/寫文件的文件系統用戶來說是透明的。盡管如此,系統仍然提供了一些工具(如df和fsck)來維護文件系統,由它們對文件系統中的塊進行操作。
HDFS同樣也有塊(block)的概念,但是大得多,默認為128MB。與單一磁盤上的文件系統相似,HDFS上的文件也被劃分為塊大小的多個分塊(chunk),作為獨立的存儲單元。但與面向單一磁盤的文件系統不同的是,HDFS中小于一個塊大小的文件不會占據整個塊的空間(例如,當一個1MB的文件存儲在一個128MB的塊中時,文件只使用1MB的磁盤空間,而不是128MB)。如果沒有特殊指出,本書中提到的“塊”特指HDFS中的塊。
HDFS中的塊為什么那么大?
HDFS的塊比磁盤的塊大,其目的是為了最小化尋址開銷。如果塊足夠大,從磁盤傳輸數據的時間會明顯大于定位這個塊開始位置所需的時間·因而,傳輸一個由多個塊組成的大文件的時間取決于磁盤傳輸速率。 我們來做一個速算,如果尋址時間約為10ms,傳輸速率為100MB/s,為了使尋址時間僅占傳輸時間的1%,我們要將塊大小設置約為100MB。默認的塊大小實際為128MB,但是很多情況下HDFS安裝時使用更大的塊。以后隨著新一代磁盤驅動器傳輸速率的提升,塊的大小會被設置得更大。但是這個參數也不會設置得過大。MapReduce中的map任務通常一次只處理一個塊中的數據,因此如果任務數太少(少于集群中的節點數量),作業的運行速度就會比較慢。 對分布式文件系統中的塊進行抽象會帶來很多好處。第一個最明顯的好處是,一個文件的大小可以大于網絡中任意一個磁盤的容量。文件的所有塊并不需要存儲在同一個磁盤上,因此它們可以利用集群上的任意一個磁盤進行存儲。事實上,盡管不常見,但對于整個HDFS集群而言,也可以僅存儲一個文件,該文件的塊占滿集群中所有的磁盤。
第二個好處是,使用抽象塊而非整個文件作為存儲單元,大大簡化了存儲子系統的設計。簡化是所有系統的目標,但是這對于故障種類繁多的分布式系統來說尤為重要。將存儲子系統的處理對象設置為塊,可簡化存儲管理(由于塊的大小是固定的,因此計算單個磁盤能存儲多少個塊就相對容易)。同時也消除了對元數據的顧慮(塊只是要存儲的大塊數據,而文件的元數據,如權限信息,并不需要與塊一同存儲,這樣一來,其他系統就可以單獨管理這些元數據)。
不僅如此,塊還非常適合用于數據備份進而提供數據容錯能力和提高可用性。將每個塊復制到少數幾個物理上相互獨立的機器上(默認為3個),可以確保在塊、磁盤或機器發生故障后數據不會丟失。如果發現一個塊不可用,系統會從其他地方讀取另一個復本,而這個過程對用戶是透明的。一個因損壞或機器故障而丟失的塊可以從其他候選地點復制到另一臺可以正常運行的機器上,以保證復本的數量回到正常水平。同樣,有些應用程序可能選擇為一些常用的文件塊設置更高的復本數量進而分散集群中的讀取負載。
與磁盤文件系統相似,HDFS中fsck指令可以顯示塊信息。例如,執行以下命令將列出文件系統中各個文件由哪些塊構成。
2. namenode和datanode
HDFS集群有兩類節點以管理節點-工作節點模式運行,即一個namenode(管理節點)和多個datanode(工作節點)。namenode管理文件系統的命名空間。它維護著文件系統樹及整棵樹內所有的文件和目錄。這些信息以兩個文件形式永久保存在本地磁盤上 :命名空間鏡像文件和編輯日志文件 。namenode也記錄著每個文件中各個塊所在的數據節點信息,但它并不永久保存塊的位置信息,因為這些信息會在系統啟動時根據數據節點信息重建。 客戶端(client)代表用戶通過與namenode和datanode交互來訪問整個文件系統。客戶端提供一個類似于POSIX(可移植操作系統界面)的文件系統接口,因此用戶在編程時無需知道namenode和datanode也可實現其功能。 datanode是文件系統的工作節點。它們根據需要存儲并檢索數據塊(受客戶端或namenode調度),并且定期向namenode發送它們所存儲的塊的列表。 沒有namenode,文件系統將無法使用。事實上,如果運行namenode服務的機器毀壞,文件系統上所有的文件將會丟失,因為我們不知道如何根據datanode的塊重建文件。因此,對namenode實現容錯非常重要,Hadoop為此提供兩種機制。 第一種機制是備份那些組成文件系統元數據持久狀態的文件。Hadoop可以通過配置使namenode在多個文件系統上保存元數據的持久狀態。這些寫操作是實時同步的,且是原子操作。一般的配置是,將持久狀態寫入本地磁盤的同時,寫入一個遠程掛載的網絡文件系統(NFS)。 另一種可行的方法是運行一個輔助namenode,但它不能被用作namenode。這個輔助namenode的重要作用是定期合并編輯日志與命名空間鏡像,以防止編輯日志過大。這個輔助namenode一般在另一臺單獨的物理計算機上運行,因為它需要占用大量CPU時間,并且需要與namenode一樣多的內存來執行合并操作。它會保存合并后的命名空間鏡像的副本,并在namenode發生故障時啟用。但是,輔助namenode保存的狀態總是滯后于主節點,所以在主節點全部失效時,難免會丟失部分數據。在這種情況下,一般把存儲在NFS上的namenode元數據復制到輔助namenode并作為新的主namenode運行。
3. 緩存塊
通常datanode從磁盤中讀取塊,但對于訪問頻繁的文件,其對應的塊可能被顯式地緩存在datanode的內存中,以堆外塊緩存(off-heap block cache)的形式存在。默認情況下,一個塊僅緩存在一個datanode的內存中,當然可以針每個文件配置datanode的數量。作業調度器(用于MapReduce、Spark和其他框架的)通過在緩存塊的datanode上運行任務,可以利用塊緩存的優勢提高讀操作的性能。例如,連接(join)操作中使用的一個小的查詢表就是塊緩存的一個很好的候選。 用戶或應用通過在緩存池(cache pool)中增加一個cache directive 來告訴namenode需要緩存哪些文件及存多久。緩存池是一個用于管理緩存權限和資源使用的管理性分組。
4. 聯邦HDFS
namenode在內存中保存文件系統中每個文件和每個數據塊的引用關系,這意味著對于一個擁有大量文件的超大集群來說,內存將成為限制系統橫向擴展的瓶頸。在2.x發行版本系列中引人的聯邦HDFS允許系統通過添加namenode實現擴展,其中每個namenode管理文件系統命名空間中的一部分。例如,一個namenode可能管理/user 目錄下的所有文件,而另一個namenode可能管理/share 目錄下的所有文件。 在聯邦環境下,每個namenode維護一個命名空間卷(namespace volume),由命名空間的元數據和一個數據塊池(block pool)組成,數據塊池包含該命名空間下文件的所有數據塊。命名空間卷之間是相互獨立的,兩兩之間并不相互通信,甚至其中一個namenode的失效也不會影響由其他namenode維護的命名空間的可用性。數據塊池不再進行切分,因此集群中的datanode需要注冊到每個namenode,并且存儲著來自多個數據塊池中的數據塊。 要想訪問聯邦HDFS集群,客戶端需要使用客戶端掛載數據表將文件路徑映射到namenode。該功能可以通過ViewFi1eSystem和 viewfs://URI進行配置和管理。
5. HDFS的高可用性
通過聯合使用在多個文件系統中備份namenode的元數據和通過備用namenode創建監測點能防止數據丟失,但是依舊無法實現文件系統的高可用性。namenode依舊存在單點失效(SPOF,single point of failure)的問題。如果namenode失效了,那么所有的客戶端,包括MapReduce作業,均無法讀、寫或列舉(list)文件,因為namenode是唯一存儲元數據與文件到數據塊映射的地方。在這一情況下,Hadoop系統無法提供服務直到有新的namenode上線。
在這樣的情況下,要想從一個失效的namenode恢復,系統管理員得啟動一個擁有文件系統元數據副本的新的namenode,并配置datanode和客戶端以便使用這個新的namenode。新的namenode直到滿足以下情形才能響應服務:
將命名空間的映像導人內存中; 重演編輯日志; 接收到足夠多的來自datanode的數據塊報告并退出安全模式。 對于一個大型并擁有大量文件和數據塊的集群,namenode的冷啟動需要30分鐘,甚至更長時間。
系統恢復時間太長,也會影響到日常維護。事實上,預期外的namenode失效出現概率很低,所以在現實中,計劃內的系統失效時間實際更為重要。
Hadoop2針對上述問題增加了對HDFS高可用性(HA)的支持。在這一實現中,配置了一對活動-備用(active-standby)namenode。當活動namenode失效,備用namenode就會接管它的任務并開始服務于來自客戶端的請求,不會有任何明顯中斷。實現這一目標需要在架構上做如下修改。
namenode之間需要通過高可用共享存儲實現編輯日志的共享。當備用namenode接管工作之后,它將通讀共享編輯日志直至末尾,以實現與活動namenode的狀態同步,并繼續讀取由活動namenode寫人的新條目。 datanode需要同時向兩個namenode發送數據塊處理報告,因為數據塊的映射信息存儲在namenode的內存中,而非磁盤。 客戶端需要使用特定的機制來處理namenode的失效問題,這一機制對用戶是透明的。 輔助namenode的角色被備用namenode所包含,備用namenode為活動的namenode命名空間設置周期性檢查點。 可以從兩種高可用性共享存儲做出選擇:NFS過濾器或群體日志管理器(QJM,quorum journal manager)。QJM是一個專用的HDFS實現,為提供一個高可用的編輯日志而設計,被推薦用于大多數HDFS部署中。QJM以一組日志節點(journalnode)的形式運行,每一次編輯必須寫入多數日志節點。典型的,有三個journal節點,所以系統能夠忍受其中任何一個的丟失。這種安排與Zookeeper的工作方式類似,當然必須認識到,QJM的實現并沒使用Zookeeper。(然而,值得注意的是,HDFS HA在選取活動的namenode時確實使用了Zookeeper技術)
在活動namenode失效之后,備用namenode能夠快速(幾十秒的時間)實現任務接管,因為最新的狀態存儲在內存中:包括最新的編輯日志條目和最新的數據塊映射信息。實際觀察到的失效時間略長一點(需要1分鐘左右),這是因為系統需要保守確定活動namenode是否真的失效了。
在活動namenode失效且備用namenode也失效的情況下,當然這類情況發生的概率非常低,管理員依舊可以聲明一個備用namenode并實現冷啟動。這類情況并不會比非高可用(non·HA)的情況更差,并且從操作的角度講這是一個進步,因為上述處理已是一個標準的處理過程并植人Hadoop中。
故障切換與規避
系統中有一個稱為故障轉移控制器(failover controller)的新實體,管理著將活動namenode轉移為備用namenode的轉換過程。有多種故障轉移控制器,但默認的一種是使用了Zookeeper來確保有且僅有一個活動namenode。每一個namenode運行著一個輕量級的故障轉移控制器,其工作就是監視宿主namenode是否失效(通過一個簡單的心跳機制實現)并在namenode失效時進行故障切換。 管理員也可以手動發起故障轉移,例如在進行日常維護時。這稱為“平穩的故障轉移”(graceful failover),因為故障轉移控制器可以組織兩個namenode有序地切換角色。 但在非平穩故障轉移的情況下,無法確切知道失效namenode是否已經停止運行。例如,在網速非常慢或者網絡被分割的情況下,同樣也可能激發故障轉移,但是先前的活動namenode依然運行著并且依舊是活動namenode。高可用實現做了更進一步的優化,以確保先前活動的namenode不會執行危害系統并導致系統崩潰的操作,該方法稱為“規避”(fencing)。 同一時間QJM僅允許一個namenode向編輯日志中寫人數據。然而,對于先前的活動namenode而言,仍有可能響應并處理客戶過時的讀請求,因此,設置一個SSH規避命令用于殺死namenode的進程是一個好主意。當使用NFS過濾器實現共享編輯日志時,由于不可能同一時間只允許一個namenode寫人數據(這也是為什么推薦QJM的原因),因此需要更有力的規避方法。規避機制包括:撤銷namenode訪問共享存儲目錄的權限(通常使用供應商指定的NFS命令)、通過遠程管理命令屏蔽相應的網絡端口。訴諸的最后手段是,先前活動namenode可以通過一個相當形象的稱為“一槍爆頭”STONITH,shoot the other node in the head)的技術進行規避,該方法主要通過一個特定的供電單元對相應主機進行斷電操作。 客戶端的故障轉移通過客戶端類庫實現透明處理。最簡單的實現是通過客戶端的配置文件實現故障轉移的控制。HDFS URI使用一個邏輯主機名,該主機名映射到一對namenode地址(在配置文件中設置),客戶端類庫會訪問每一個namenode地址直至處理完成。
4. 命令行接口
現在我們通過命令行交互來進一步認識HDFS。HDFS還有很多其他接口,但命令行是最簡單的,同時也是許多開發者最熟悉的。 參照附錄A中偽分布模式下設置Hadoop的說明,我們先在一臺機器上運行HDFS。稍后介紹如何在集群上運行HDFS,以提供可擴展性與容錯性。 在我們設置偽分布配置時,有兩個屬性項需要進一步解釋。第一項是fs.defaultFS,設置為hdfs://localhost/,用于設置Hadoop的默認文件系統。文件系統是由URI指定的,這里我們已使用hdfs URI來配置HDFS為Hadoop的默認文件系統。HDFS的守護程序通過該屬性項來確定HDFS namenode的主機及端口。我們將在localhost默認的HDFS端口8020上運行namenode。樣一來,HDFS客戶端可以通過該屬性得知namenode在哪里運行進而連接到它。 第二個屬性dfs.replication,我們設為1,這樣一來,HDFS就不會按默認設置將文件系統塊復本設為3。在單獨一個datanode上運行時,HDFS無法將塊復制到3個datanode上,所以會持續給出塊復本不足的警告。設置這個屬性之后,上述問題就不會再出現了。
1. 文件系統的基本操作
至此,文件系統已經可以使用了,我們可以執行所有常用的文件系統操作,例如,讀取文件,新建目錄,移動文件,刪除數據,列出目錄,等等。可以輸人hadoop fs -help命令獲取每個命令的詳細幫助文件。
首先從本地文件系統將一個文件復制到HDFS:
%hadoop fs - copyFromLocal input/ docs/ quangle. txt \ hdfs: / / localhost/ user/ tom/ quangle. txt
該命令調用Hadoop文件系統的shell命令fs,后者提供了一系列子命令,在這個例子中,我們執行的是-copyFromLocal。本地文件quangle.txt被復制到運行在localhost上的HDFS實例中,路徑為/user/tom/quangle.txt。事實上,我們可以簡化命令格式以省略主機的URI并使用默認設置,即省略hdfs://localhost,因為該項已在core-site.xml中指定。
%hadoop fs - copyFromLoca1 input/ docs/ quangle. txt / user/ tom/ quangle. txt
我們也可以使用相對路徑,并將文件復制到HDFS的home目錄中,本例中為
%hadoop fs - copyFromLoca1 input/ docs/ quangle. txt quangle.txt
我們把文件復制回本地文件系統,并檢查是否一致:
%hadoop fs - copyToLocal quangle. txt quangle. copy. txt
%md5 input/ docs/ quangle. txt quangle. copy. txt
MD5 ( input/ docs/ quangle. txt) = e7891a2627cf263a979fb9f18256ffb2
MD5 ( quangle. copy. txt) = e7891a2627cf263a979fb9f18256ffb2
MD5鍵值相同,表明這個文件在HDFS之旅中得以幸存并保存完整。
最后,看一下HDFS文件列表。我們新建一個目錄,看它在列表中怎么顯示:
%hadoop fs - mkdir books
%hadoop fs - ls .
8. 返回的結果信息與Unix命令Is -l 的輸出結果非常相似,僅有細微差別。第1列顯示的是文件模式。第2列是這個文件的備份數(這在傳統Unix文件系統是沒有的)。由于我們在整個文件系統范圍內設置的默認復本數為1,所以這里顯示的也都是1。這一列的開頭目錄為空,因為本例中沒有使用復本的概念,目錄作為元數據保存在namenode中,而非datanode中。第3列和第4列顯示文件的所屬用戶和組別。第5列是文件的大小,以字節為單位,目錄為0。第6列和第7列是文件的最后修改日期與時間。最后,第8列是文件或目錄的名稱。
2. HDFS中的文件訪問權限
針對文件和目錄,HDFS的權限模式與POSIX的權限模式非常相似。 一共提供三類權限模式:只讀權限?、寫入權限(w)和可執行權限(x)。讀取文件或列出目錄內容時需要只讀權限。寫入一個文件或是在一個目錄上新建及刪除文件或目錄,需要寫入權限。對于文件而言,可執行權限可以忽略,因為你不能在HDFS中執行文件(與POSIX不同),但在訪問一個目錄的子項時需要該權限。 每個文件和目錄都有所屬用戶(owner)、所屬組別(group)及模式(mode)。這個模式是由所屬用戶的權限、組內成員的權限及其他用戶的權限組成的。 在默認情況下,Hadoop運行時安全措施處于停用模式,意味著客戶端身份是沒有經過認證的。由于客戶端是遠程的,一個客戶端可以在遠程系統上通過創建和任一個合法用戶同名的賬號來進行訪問。當然,如果安全設施處于啟用模式,這些都是不可能的。無論怎樣,為防止用戶或自動工具及程序意外修改或刪除文件系統的重要部分,啟用權限控制還是很重要的(這也是默認的配置,參見dfs.permissions.enabled屬性) 如果啟用權限檢查,就會檢查所屬用戶權限,以確認客戶端的用戶名與所屬用戶是否匹配,另外也將檢查所屬組別權限,以確認該客戶端是否是該用戶組的成員;若不符,則檢查其他權限。 這里有一個超級用戶(super-user)的概念,超級用戶是namenode進程的標識·對于超級用戶,系統不會執行任何權限檢查。
5. Hadoop文件系統
Hadoop有一個抽象的文件系統概念,HDFS只是其中的一個實現。Java抽象類org.apache.hadoop.fs.FileSystem定義了Hadoop中一個文件系統的客戶端接口,并且該抽象類有幾個具體實現,其中和Hadoop緊密相關的見表3.1。
Hadoop對文件系統提供了許多接口,它一般使用URI方案來選取合適的文件系統實例進行交互。舉例來說,我們在前一小節中遇到的文件系統命令行解釋器可以操作所有的Hadoop文件系統命令。要想列出本地文件系統根目錄下的文件,可以輸人以下命令:
%hadoop fs - ls file: /
盡管運行的MapReduce程序可以訪問任何文件系統(有時也很方便),但在處理大數據集時,建議你還是選擇一個有數據本地優化的分布式文件系統,如HDFS。
1. 接口
Hadoop是用Java寫的,通過Java API可以調用大部分Hadoop文件系統的交互操作。例如,文件系統的命令解釋器就是一個Java應用,它使用Java的FileSystem類來提供文件系統操作。其他一些文件系統接口也將在本小節中做簡單介紹。這些接口通常與HDFS一同使用,因為Hadoop中的其他文件系統一般都有訪問基本文件系統的工具(對于FTP,有FTP客戶端;對于S3,有S3工具,等等),但它們大多數都能用于任何Hadoop文件系統。
1. HTTP
Hadoop以Java API的形式提供文件系統訪問接口,非Java開發的應用訪問HDFS會很不方便。由Web HDFS協議提供的HTTP REST API則使得其他語言開發的應用能夠更方便地與HDFS交互。注意,HTTP接口比原生的Java客戶端要慢,所以不到萬不得已,盡量不要用它來傳輸特大數據。 通過HTTP來訪問HDFS有兩種方法:直接訪問,HDFS守護進程直接服務于來自客戶端的HTTP請求;通過代理(一個或多個)訪問,客戶端通常使用DistributedFileSystem API訪問HDFS。這兩種方法如圖所示。兩者都使用了WebHDFS協議。 在第一種情況中,namenode和datanode內嵌的web服務器作為WebHDFS的端節點運行。(由于dfs.webhdfs.enabled被設置為true,WebHDFS默認是啟用狀態。)文件元數據操作由namenode管理,文件讀(寫)操作首先被發往namenode,由namenode發送一個HTTP重定向至某個客戶端,指示以流方式傳輸文件數據的目的或源datanode。 第二種方法依靠一個或者多個獨立代理服務器通過HTTP訪問HDFS。(由于代理服務是無狀態的,因此可以運行在標準的負載均衡器之后。)所有到集群的網絡通信都需要經過代理,因此客戶端從來不直接訪問namenode或datanode。使用代理服務器后可以使用更嚴格的防火墻策略和帶寬限制策略。通常情況下都通過代理服務器,實現在不同數據中心中部署的Hadoop集群之間的數據傳輸,或從外部網絡訪問云端運行的Hadoop集群。 HttpFS 代理提供和WebHDFS相同的HTTP(和HTTPS)接口,這樣客戶端能夠通過webhdfs(swebhdfs) URI訪問這兩類接口。HttpFS代理的啟動獨立于namenode和datanode的守護進程,使用httpfs.sh腳本,默認在一個不同的端口上監聽(端口號14000)。
2. C語言
Hadoop提供了一個名為libhdfs的C語言庫,該語訁庫是Java Filesystem接口類的一個鏡像(它被寫成訪問HDFS的C語言庫,但其實它可以訪問任何一個Hadoop文件系統)。它使用Java原生接口(JNI,Java Native Interface)調用Java文件系統客戶端。同樣還有一個libwebfhdfs庫,該庫使用了前述章節描述的WebHDFS接口。 這個C語言API與Java的API非常相似,但它的開發滯后于JavaAPI,因此目前一些新的特性可能還不支持。可以在Apache Hapdoop二進制壓縮發行包的include目錄中找到頭文件hdfs.h。 Apache Hapdoop二進制壓縮包自帶預先編譯好的libhdfs二進制編碼,支持64位Linux。但對于其他平臺,需要按照源代碼樹頂層的BUILDING.txt指南自行編譯。
3. NFS
使用Hadoop的NFSv3網關將HDFS掛載為本地客戶端的文件系統是可行的。然后你可以使用Unix實用程序(如Is和cat)與該文件系統交互,上傳文件,通過任意一種編程語言調用POSIX庫來訪問文件系統。由于HDFS僅能以追加模式寫文件,因此可以往文件末尾添加數據,但不能隨機修改文件。 關于如何配置和運行NFS網關,以及如何從客戶端連接網關,可以參考Hadoop相關文檔資料。
4. FUSE
用戶空間文件系統(FUSE,Filesystem in Userspace,)允許將用戶空間實現的文件系統作為Unix文件系統進行集成。通過使用Hadoop的Fuse-DFS功能模塊,HDFS(或任何一個Hadoop文件系統)均可以作為一個標準的本地文件系統進行掛載。Fuse-DFS是用C語言實現的,使用libhdfs作為訪問HDFS的接口。在寫操作時,Hadoop NFS網關對于掛載HDFS來說是更健壯的解決方案,相比Fuse-DFS而言應優先選擇。
6. JAVA接口
有時間補上。
7. 數據流
1. 剖析文件讀取
為了了解客戶端及與之交互的HDFS、namenode和datanode之間的數據流是什么樣的,我們可參考下圖,該圖顯示了在讀取文件時事件的發生順序。 客戶端通過調用Filesyste對象的open()方法來打開希望讀取的文件,對于HDFS來說,這個對象是DistributedFileSystem的一個實例(下圖中的步驟1)。DistributedFileSystem通過使用遠程過程調用(RPC)來調用namenode,以確定文件起始塊的位置(步驟2)。對于每一個塊,namenode返回存有該塊副本的datanode地址。此外,這些datanode根據它們與客戶端的距離來排序(根據集群的網絡拓撲)。如果該客戶端本身就是一個datanode(比如,在一個MapReduce任務中),那么該客戶端將會從保存有相應數據塊復本的本地datanode讀取數據。 DistributedFileSystem類返回一個FSDataInputStream對象(一個支持文件定位的輸人流)給客戶端以便讀取數據。FSDatainputstream類轉而封裝DFSInputStream對象,該對象管理著datanode和namenode的I/0。 接著,客戶端對這個輸人流調用read()方法(步驟3)。存儲著文件起始幾個塊的datanode地址的DFSInputstream隨即連接距離最近的文件中第一個塊所在的datanode。通過對數據流反復調用read()方法,可以將數據從datanode傳輸到客戶端(步驟4)。到達塊的末端時,DFSlnputStream關閉與該datanode的連接,然后尋找下一個塊的最佳datanode(步驟5)。所有這些對于客戶端都是透明的,在客戶看來它一直在讀取一個連續的流。 客戶端從流中讀取數據時,塊是按照打開DFSInputstream與datanode新建連接的順序讀取的。它也會根據需要詢問namenode來檢索下一批數據塊的datanode的位置。一旦客戶端完成讀取,就對調用close()方法(步驟6)。 在讀取數據的時候,如果DFSInputStream在與datanode通信時遇到錯誤,會嘗試從這個塊的另外一個最鄰近datanode讀取數據。它也記住那個故障datanode,以保證以后不會反復讀取該節點上后續的塊。DFSInputStream也會通過校驗和確認從datanode發來的數據是否完整。如果發現有損壞的塊,DFSInputStream會試圖從其他datanode讀取其復本,也會將被損壞的塊通知給namenode。 這個設計的一個重點是,客戶端可以直接連接到datanode檢索數據,且namenode告知客戶端每個塊所在的最佳datanode。由于數據流分散在集群中的所有datanode,所以這種設計能使HDFS擴展到大量的并發客戶端。同時,namenode只需要響應塊位置的請求(這些信息存儲在內存中,因而非常高效),無需響應數據請求,否則隨著客戶端數量的增長,namenode會很快成為瓶頸。
網絡擴撲與Hadoop
在本地網絡中,兩個節點被稱為“彼此近鄰”是什么意思?在海量數據處理中,其主要限制因素是節點之間數據的傳輸速率一一帶寬很稀缺。這里的想法是將兩個節點間的帶寬作為距離的衡量標準。 不用衡量節點之間的帶寬,實際上很難實現(它需要一個穩定的集群,并且在集群中兩兩節點對數量是節點數量的平方),hadoop為此采用一個簡單的方法:把網絡看作一棵樹,兩個節點間的距離是它們到最近共同祖先的距離總和。該樹中的層次是沒有預先設定的,但是相對于數據中心、機架和正在運行的節點,通常可以設定等級。具體想法是針對以下每個場景,可用帶寬依次遞減: 同一節點上的進程 同一機架上的不同節點 同一數據中心中不同機架上的節點 不同數據中心中的節點. 例如,假設有數據中心d1機架r1中的節點n1。該節點可以表示為/d1/r1/n1。利用這種標記,這里給出四種距離描述: distance(/d1/r1/n1,/d1/r1/n1)=0(同一節點上的進程) distance(/d1/r1/n1,/d1/r1/n2)=2(同一機架上的不同節點) distance(/d1/r1/n1,/d1/r2/n3)=4(同一數據中心中不同機架上的節點) distance(/d1/r1/n1,/d1/r1/n1)=6(不同數據中心中的節點) 示意圖參見下圖(數學愛好者會注意到,這是一個測量距離的例子)。 最后,我們必須意識到Hadoop無法自動發現你的網絡拓撲結構。它需要一些幫助(我們將在10.1.2節的“網絡拓撲”中討論如何配置網絡拓撲)。不過在默認情況下,假設網絡是扁平化的只有一層,或換句話說,所有節點都在同一數據中心的同一機架上。規模小的集群可能如此,不需要進一步配置。
2. 剖析文件寫入
接下來我們看看文件是如何寫人HDFS的。盡管比較詳細,但對于理解數據流還是很有用的,因為它清楚地說明了HDFS的一致模型。我們要考慮的情況是如何新建一個文件,把數據寫人該文件,最后關閉該文件。如下圖所示。 客戶端通過對DistributedFileSystem對象調用create()來新建文件(圖中的步驟1)。DistributedFileSystem對namenode創建一個RPC調用,在文件系統的命名空間中新建一個文件,此時該文件中還沒有相應的數據塊(步驟2)。namenode執行各種不同的檢查以確保這個文件不存在以及客戶端有新建該文件的權限。如果這些檢查均通過,namenode就會為創建新文件記錄一條記錄;否則,文件創建失敗并向客戶端拋出一個I0Exception異常。DistributedFileSystem向客戶端返回一個FSData0utputStream對象,由此客戶端可以開始寫人數據。就像讀取事件一樣,FSDataOutputStream封裝一個DFSoutputstream對象,該對象負責處理datanode和namenode之間的通信。 在客戶端寫人數據時(步驟3),DFSOutputStream將它分成一個個的數據包,并寫人內部隊列,稱為“數據隊列”(data queue)。DataStreamer處理數據隊列,它的責任是挑選出適合存儲數據復本的一組datanode,并據此來要求namenode分配新的數據塊。這一組datanode構成一個管線一一我們假設復本數為3,所以管線中有3個節點。DataStreamer將數據包流式傳輸到管線中第1個datanode,該datanode存儲數據包并將它發送到管線中的第2個datanode。同樣,第2個datanode存儲該數據包并且發送給管線中的第3個(也是最后一個)datanode(步驟4)。 DFSOutputStream也維護著一個內部數據包隊列來等待datanode的收到確認回執,稱為“確認隊列”(ackqueue)。收到管道中所有datanode確認信息后,該數據包才會從確認隊列刪除(步驟5)。 如果任何datanode在數據寫人期間發生故障,則執行以下操作(對寫入數據的客戶端是透明的)。首先關閉管線,確認把隊列中的所有數據包都添加回數據隊列的最前端,以確保故障節點下游的datanode不會漏掉任何一個數據包。為存儲在另一正常datanode的當前數據塊指定一個新的標識,并將該標識傳送給namenode,以便故障datanode在恢復后可以刪除存儲的部分數據塊。從管線中刪除故障datanode,基于兩個正常datanode構建一條新管線。余下的數據塊寫人管線中正常的datanodeonamenode注意到塊復本量不足時,會在另一個節點上創建一個新的復本。后續的數據塊繼續正常接受處理。 在一個塊被寫人期間可能會有多個datanode同時發生故障,但非常少見。只要寫人了dfs.namenode.replication.min的復本數(默認為1),寫操作就會成功,并且這個塊可以在集群中異步復制,直到達到其目標復本數@fs,repli(ation的默認值為3)。 客戶端完成數據的寫人后,對數據流調用close()方法(步驟6)。該操作將剩余的所有數據包寫人datanode管線,并在聯系到namenode告知其文件寫入完成之前,等待確認(步驟7)。namenode已經知道文件由哪些塊組成(因為Datastreamer請求分配數據塊),所以它在返回成功前只需要等待數據塊進行最小量的復制。
復本怎么放
namenode如何選擇在哪個datanode存儲復本(replica)?這里需要對可靠性、寫入帶寬和讀取帶寬進行權衡。例如,把所有復本都存儲在一個節點損失的寫入帶寬最小(因為復制管線都是在同一節點上運行),但這并不提供真實的冗余(如果節點發生故障,那么該塊中的數據會丟失)。同時,同一機架上服務器間的讀取帶寬是很高的。另一個極端,把復本放在不同的數據中心可以最大限度地提高冗余,但帶寬的損耗非常大。即使在同一數據中心(到目前為止,,所有Hadoop集群均運行在同一數據中心內),也有多種可能的數據布局策喀。 Hadoop的默認布局策略是在運行客戶端的節點上放第1個復本(如果客戶端運行在集群之外,就隨機選擇一個節點,不過系統會避免挑選那些存儲太滿或太忙的節點)。第2個復本放在與第一個不同且隨機另外選擇的機架中節點上(離架)。第3個復本與第2個復本放在同一個機架上,且隨機選擇另一個節點。其他復本放在集群中隨機選擇的節點上,不過系統會盡量避免在同一個的機架上放太多復本。 一旦選定復本的放置位置,就根據網絡拓撲創建一個管線。如果復本數為3,則有下圖所示的管線。 總的來說,這一方法不僅提供很好的穩定性(數據塊存儲在兩個機架中)并實現很好的負載均衡,包括寫入帶寬(寫入操作只需要遍歷一個交換機)、讀取性能(可以從兩個機架中選擇讀取)和集群中塊的均勻分布(客戶端只在本地機架上寫入一個塊)。
3. 一致性模型
文件系統的一致模型(coherency model)描述了文件讀/寫的數據可見性。HDFS為性能犧牲了一些POSIX要求,因此一些操作與你期望的可能不同。
新建一個文件之后,它能在文件系統的命名空間中立即可見,如下所示:
但是,寫人文件的內容并不保證能立即可見,即使數據流已經刷新并存儲。所以文件長度顯示為0:
當寫人的數據超過一個塊后,第一個數據塊對新的reader就是可見的。之后的塊也不例外。總之,當前正在寫人的塊對其他reader不可見。
HDFS提供了一種強行將所有緩存刷新到datanode中的手段,即對FSDataOutputStream調用hflush()方法。當hflush()方法返回成功后,對所有新的reader而言,HDFS能保證文件中到目前為止寫人的數據均到達所有datanode的寫人管道并且對所有新的reader均可見:
注意,hflush()不保證datanode已經將數據寫到磁盤上,僅確保數據在datanode的內存中(因此,如果數據中心斷電,數據會丟失)。為確保數據寫人到磁盤上,可以用hsync()替代.
hsync()操作類似于POSIX中的fsync()系統調用,該調用提交的是一個文件描述符的緩沖數據。例如,利用標準Java API數據寫人本地文件,我們能夠在刷新數據流且同步之后看到文件內容:
在HDFS中關閉文件其實隱含了執行hflush()方法 待續…
總結
以上是生活随笔 為你收集整理的《Hadoop权威指南》第三章 Hadoop分布式文件系统 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。