HDFS——HDFS+Zookeeper搭建高可用HDFS

問題說明

NameNode是HDFS集羣的單點故障點,每個集羣只有一個NameNode,若是這個機器或進程不可用,整個集羣就沒法使用。爲解決這一問題提供了兩種解決方法:NFS(採用網絡共享文件模式)和QJM(HDFS使用Quorum Journal Manager來共享Action NameNode與Standby NameNode之間的edit logs)。node

圖 HDFS+Zookeeper實現高可用架構算法

總體架構

在一個HA集羣中,會配置兩個獨立的NameNode。在任意時刻,只有一個NameNode做爲活動的節點,另外一個節點則處於備份狀態。活動的NameNode負責執行全部修改命名空間以及刪除備份數據塊的操做,而備份的NameNode則執行同步操做,以保持與活動節點命名空間的一致性。 promise

爲了使備份節點與活動節點的狀態可以同步一致,兩個節點都須要同一組獨立運行的節點(JournalNodes,JNS)通訊。當Active NameNode執行了修改命名空間的操做時,它會按期將執行的操做記錄保存在在editlog中,並寫入JNS的多數節點中。而Standby NameNode會一直監聽JNS上editlog的變化,若是發現editlog有改動,Standby NameNode就會讀取editlog並與當前的命名空間合併。當發生了錯誤切換時,Standby節點會保證已經從JNS上讀取了全部editlog並與命名空間合併,而後纔會從Standby狀態切換爲Active狀態。經過這種機制,保證了Active NameNode與Standby NameNode之間命名空間狀態的一致性,也就是第一關係鏈的一致性。 緩存

爲了使錯誤切換可以很快的執行完畢,就要保證Standby節點也保存了實時的block的存儲信息,也就是第二關係鏈。這樣發生錯誤切換時,Standby節點就不須要等待全部的數據節點進行全量數據塊彙報,而直接能夠切換到Active狀態。爲了實現這個機制,DataNode會同時向這兩個NameNode發送心跳以及塊彙報信息。這樣就實現了Active NameNode 和standby NameNode 的元數據就徹底一致,一旦發生故障,就能夠立刻切換,也就是熱備。網絡

這裏須要注意的是 Standby NameNode只會更新數據塊的存儲信息,並不會向NameNode 發送複製或者刪除數據塊的指令,這些指令只能由Active NameNode發送。 架構

在HA架構中有一個很是重非要的問題,就是須要保證同一時刻只有一個處於Active狀態的NameNode,不然機會出現兩個NameNode同時修改命名空間的問題,也就是腦裂(Split-brain)。腦裂的HDFS集羣極可能形成數據塊的丟失,以及向DataNode下發錯誤的指令等異常狀況。爲了預防腦裂的狀況,HDFS提供了三個級別的隔離機制(fencing): 分佈式

  1. 共享存儲隔離:同一時間只容許一個NameNode向JournalNodes寫入editlog數據。 
  2. 客戶端隔離:同一時間只容許一個NameNode響應客戶端的請求。 
  3. Datanode隔離:同一時間只容許一個NameNode向DataNode下發名字節點指令,例如刪除、複製數據塊指令等等。

在HA實現中還有一個很是重要的部分就是Active NameNode和Standby NameNode之間如何共享editlog日誌文件。Active NameNode會將日誌文件寫到共享存儲上。Standby NameNode會實時的從共享存儲讀取editlog文件,而後合併到Standby NameNode的命名空間中。這樣一旦Active NameNode發生錯誤,Standby NameNode能夠當即切換到Active狀態。在Hadoop2.6中,提供了QJM(Quorum Journal Manager)方案來解決HA共享存儲問題。oop

全部的HA實現方案都依賴於一個保存editlog的共享存儲,這個存儲必須是高可用的,而且可以被集羣中全部的NameNode同時訪問。Quorum Journal是一個基於paxos算法的HA設計方案。 性能

Quorum Journal

Quorum Journal方案中有兩個重要的組件。 url

  1. JournalNoe(JN):運行在N臺獨立的物理機器上,它將editlog文件保存在JournalNode的本地磁盤上,同時JournalNode還對外提供RPC接口QJournalProtocol以執行遠程讀寫editlog文件的功能。 
  2. Quorum Journal Manager(QJM):運行在NameNode上,(目前HA集羣只有兩個NameNode),經過調用RPC接口QJournalProtocol中的方法向JournalNode發送寫入、排斥、同步editlog。

Quorum Journal方案依賴於這樣一個概念:HDFS集羣中有2N+1個JN存儲editlog文件,這些editlog 文件是保存在JN的本地磁盤上的。每一個JN對QJM暴露QJM接口QJournalProtocol,容許NameNode讀寫editlog文件。當NameNode向共享存儲寫入editlog文件時,它會經過QJM向集羣中全部的JN發送寫editlog文件請求,當有一半以上的JN返回寫操做成功時,即認爲寫成功。這個原理是基於Paxos算法的。

使用Quorum Journal實現的HA方案有一下優勢: 

  1. JN進程能夠運行在普通的PC上,而無需配置專業的共享存儲硬件。 
  2. 不須要單獨實現fencing機制,Quorum Journal模式中內置了fencing功能。 
  3. Quorum Journal不存在單點故障,集羣中有2N+1個Journal,能夠容許有N個Journal Node死亡。 
  4. JN不會由於其中一個機器的延遲而影響總體的延遲,並且也不會由於JN數量的增多而影響性能(由於NameNode向JournalNode發送日誌是並行的)

互斥機制

當HA集羣中發生NameNode異常切換時,須要在共享存儲上fencing上一個活動的節點以保證該節點不能再向共享存儲寫入editlog。基於Quorum Journal模式的HA提供了epoch number來解決互斥問題,這個概念能夠在分佈式文件系統中找到。epoch number具備如下幾個性質。 
1.當一個NameNode變爲活動狀態時,會分配給他一個epoch number。 
2.每一個epoch number都是惟一的,沒有任意兩個NameNode有相同的epoch number。 
3.epoch number 定義了NameNode寫editlog文件的順序。對於任意兩個NameNode ,擁有更大epoch number的NameNode被認爲是活動節點。

當一個NameNode切換爲活動狀態時,它的QJM會向全部的JN發送命令,以獲取該JN的最後一個promise epoch變量值。當QJM接受到了集羣中多於一半的JN回覆後,它會將所接收到的最大值加一,並保存到myepoch 中,以後QJM會將該值發送給全部的JN並提出更新請求。每一個JN會將該值與自身的epoch值相互比較,若是新的myepoch比較大,則JN更新,並返回更新成功;若是小,則返回更新失敗。若是QJM接收到超過一半的JN返回成功,則設置它的epoch number爲myepoch;不然它終止嘗試爲一個活動的NameNode,並拋出異常。

當活動的NameNode成功獲取並更新了epoch number後,調用任何修改editlog的RPC請求都必須攜帶epoch number。當RPC請求到達JN後,JN會將請求者的epoch與自身保存的epoch相互對比,若請求者的epoch更大,JN就會更新本身的epoch,並執行相應的操做,若是請求者的epoch小,就會拒絕相應的請求。當集羣中大多數的JN拒絕了請求時,此次操做就失敗了。 
當HDFS集羣發生NameNode錯誤切換後,原來的standby NameNode將集羣的epoch number加一後更新。這樣原來的Active NameNode的epoch number確定小於這個值,當這個節點執行寫editlog操做時,因爲JN節點不接收epoch number小於自身的promise epoch的寫請求,因此此次寫請求會失敗,也就達到了fencing的目的。

editlog寫流程 

  1. 將editlog輸出流中緩存的數據寫入JN,對於集羣中的每個JN都存在一個獨立的線程調用RPC 接口中的方法向JN寫入數據。 
  2. 當JN收到請求以後,JN會執行如下操做: 
    1. 驗證epoch number是否正確 
    2. 確認寫入數據對應的txid是否連續 
    3. 將數據持久化到JN的本地磁盤 
    4. 向QJM發送正確的響應
  3. QJM等待集羣JN的響應,若是多數JN返回成功,則寫操做成功;不然寫操做失敗,QJM會拋出異常。
  • NameNode會調用FSEditlogLog下面的方法初始化editlog文件的輸出流,而後使用輸出流對象向editlog文件寫入數據。 
  • 獲取了QuorumOutputStream輸出流對象以後,NameNode會調用write方法向editlog文件中寫入數據,QuorumOutputStream的底層也調用了EditsDoubleBuffer雙緩存區。數據回先寫入其中一個緩衝區中,而後調用flush方法時,將緩衝區中的數據發送給JN。

讀流程 

Standby NameNode會從JN讀取editlog,而後與Sdtandby NameNode的命名空間合併,以保持和Active NameNode命名空間的同步。當Sdtandby NameNode從JN讀取editlog時,它會首先發送RPC請求到集羣中全部的JN上。JN接收到這個請求後會將JN本地存儲上保存的全部FINALIZED狀態的editlog段落文件信息返回,以後QJM會爲全部JN返回的editlog段落文件構造輸入流對象,並將這些輸入流對象合併到一個新的輸入流對象中,這樣Standby namenode就能夠從任一個JN讀取每一個editlog段落了。若是其中一個JN失敗了輸入流對象會自動切換到另外一個保存了該edirlog段落的JN上。 

恢復流程 

當NameNode發生主從切換時,原來的Standby NameNode會接管共享存儲並執行寫editlog的操做。在切換以前,對於共享存儲會執行如下操做: 

  1. fencing原來的Active NameNode。這部分在互斥部分已經講述。 
  2. 恢復正在處理的editlog。因爲NameNode發生了主從切換,集羣中JN上正在執行寫入操做的editlog數據可能不一致。例如,可能出現某些JN上的editlog正在寫入,可是當前Active NameNode發生錯誤,這時該JN上的editlog文件就與已完成寫入的JN不一致。在這種狀況下,須要對JN上全部狀態不一致的editlog文件執行恢復操做,將他們的數據同步一致,而且將editlog文件轉化爲FINALIZED狀態。 
  3. 當不一致的editlog文件完成恢復以後,這時原來的Standby NameNode就能夠切換爲Active NameNode並執行寫editlog的操做。 
  4. 寫editlog。在前面已經介紹了。

日誌恢復操做

日誌恢復操做能夠分爲如下幾個階段: 

  1. 肯定須要執行恢復操做的editlog段落:在執行恢復操做以前,QJM會執行newEpoch()調用以產生新的epoch number,JN接收到這個請求後除了執行更新epoch number外,還會將該JN上保存的最新的editlog段落的txid返回。當集羣中的大多數JN都發回了這個響應後,QJM就能夠肯定出集羣中最新的一個正在處理editlog段落的txid,而後QJM就會對這個txid對應的editlog段落執行恢復操做了。
  2. 準備恢復:QJM向集羣中的全部JN發送RPC請求,查詢執行恢復操做的editlog段落文件在全部JN上的狀態,這裏的狀態包括editlog文件是in-propress仍是FINALIZED狀態,以及editlog文件的長度。
  3. 接受恢復:QJM接收到JN發回的JN發回的響應後,會根據恢復算法選擇執行恢復操做的源節點。而後QJM會發送RPC請求給每個JM,這個請求會包含兩部分信息:源editlog段落文件信息,以及供JN下載這個源editlog段落的url。 
  4. 接收到這個RPC請求以後,JN會執行如下操做: 
    1. 同步editlog段落文件,若是JN磁盤上的editlog段落文件與請求中的段落文件狀態不一樣,則JN會從當前請求中的url上下載段落文件,並替換磁盤上的editlog段落文件。 
    2. 持久化恢復元數據,JN會將執行恢復操做的editlog段落文件的狀態、觸發恢復操做的QJM的epoch number等信息(恢復的元數據信息)持久化到磁盤上。 
    3. 當這些操做都執行成功後,JN會返回成功響應給QJM,若是集羣中的大多數JN都返回了成功,則這次恢復操做執行成功。
  5. 完成editlog段落文件:到這步操做時,QJM 就能肯定集羣中大多數的JN保存的editlog文件的狀態已經一致了,而且JN持久化了恢復信息。QJM就會向JN發送指令,將這個editlog段落文件的狀態轉化爲FINALIZED狀態,,而且JN會刪除持久化的恢復元數據,由於磁盤上保存的editlog文件信息已是正確的了,不須要保存恢復的元數據。

HDFS的高可用機制詳解       

相關文章
相關標籤/搜索