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): 分佈式
在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方案中有兩個重要的組件。 url
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方案有一下優勢:
當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的目的。
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的操做。在切換以前,對於共享存儲會執行如下操做:
日誌恢復操做能夠分爲如下幾個階段: