Hadoop1.x:
Hadoop 的兩大核心組件 HDFS 的NameNode 和 JobTracker 都存在著單點(diǎn)問(wèn)題。
Hadoop2.x :
HDFS的NameNode 和 YARN的ResourceManger 的單點(diǎn)問(wèn)題可以解決。
SecondaryNameNode保存的狀態(tài)總是滯后于NameNode,所以這種方式難免會(huì)導(dǎo)致丟失部分?jǐn)?shù)據(jù),也可以解決。(NameNode和ResourceManger實(shí)現(xiàn)方式類似,前者更復(fù)雜)
下面主要講HDFS NameNode的高可用(下圖為HDFS NameNode 高可用架構(gòu)圖):

概念
Active NameNode 和 Standby NameNode:
兩臺(tái) NameNode 形成互備,一臺(tái)處于 Active 狀態(tài),為主 NameNode,另外一臺(tái)處于 Standby 狀態(tài),為備 NameNode,只有主 NameNode 才能對(duì)外提供讀寫(xiě)服務(wù)。
Active NN負(fù)責(zé)集群中所有客戶端的操作;
Standby NN主要用于備用,它主要維持足夠的狀態(tài),如果必要,可以提供快速
的故障恢復(fù)。
FailoverController(主備切換器):
FailoverController 作為獨(dú)立的進(jìn)程運(yùn)行,對(duì) NameNode 的主備切換進(jìn)行總體控制。FailoverController 監(jiān)測(cè) NameNode 的健康狀況,在主 NameNode 故障時(shí)借助 Zookeeper 實(shí)現(xiàn)自動(dòng)的主備選舉和切換。
HealthMonitor :
HealthMonitor是一個(gè)周期性工作的后臺(tái)線程,它在一行個(gè)循環(huán)中周期性的同HA服務(wù)進(jìn)心跳,負(fù)責(zé)跟蹤NameNode服務(wù)的健康狀況,并在健康狀況變化時(shí)調(diào)用failover控制器的回調(diào)方法。
HealthMonitor 主要負(fù)責(zé)檢測(cè) NameNode 的健康狀態(tài),如果檢測(cè)到 NameNode 的狀態(tài)發(fā)生變化,會(huì)回調(diào) ZKFailoverController 的相應(yīng)方法進(jìn)行自動(dòng)的主備選舉。
ActiveStandbyElector
ActiveStandbyElector 主要負(fù)責(zé)完成自動(dòng)的主備選舉,內(nèi)部封裝了 Zookeeper 的處理邏輯,一旦 Zookeeper 主備選舉完成,會(huì)回調(diào) ZKFailoverController 的相應(yīng)方法來(lái)進(jìn)行 NameNode 的主備狀態(tài)切換。
共享存儲(chǔ)系統(tǒng):
共享存儲(chǔ)系統(tǒng)是實(shí)現(xiàn) NameNode 的高可用最為關(guān)鍵的部分,共享存儲(chǔ)系統(tǒng)保存了 NameNode 在運(yùn)行過(guò)程中所產(chǎn)生的 HDFS 的元數(shù)據(jù)。主 NameNode 和備NameNode 通過(guò)共享存儲(chǔ)系統(tǒng)實(shí)現(xiàn)元數(shù)據(jù)同步。在進(jìn)行主備切換的時(shí)候,新的主 NameNode 在確認(rèn)元數(shù)據(jù)完全同步之后才能繼續(xù)對(duì)外提供服務(wù)。
使用JN(journalnode JQM方式):

Qurom Journal Manager,基于Paxos(基于消息傳遞的一致性算法)。Paxos算法是解決分布式環(huán)境中如何就某個(gè)值達(dá)成一致(一個(gè)典型的場(chǎng)景是,在一個(gè)分布式數(shù)據(jù)庫(kù)系統(tǒng)中,如果各節(jié)點(diǎn)的初始狀態(tài)一致,每個(gè)節(jié)點(diǎn)都執(zhí)行相同的操作序列,那么他們最后能得到一個(gè)一致的狀態(tài)。為保證每個(gè)節(jié)點(diǎn)執(zhí)行相同的命令序列,需要在每一條指令上執(zhí)行一個(gè)"一致性算法"以保證每個(gè)節(jié)點(diǎn)看到的指令一致)。
實(shí)現(xiàn)方式:
(1) 初始化后,Active把editlog日志寫(xiě)到2N+1上JN上,每個(gè)editlog有一個(gè)編號(hào),每次寫(xiě)editlog只要其中大多數(shù)JN返回成功(即大于等于N+1)即認(rèn)定寫(xiě)成功。
(2) Standby定期從JN讀取一批editlog,并應(yīng)用到內(nèi)存中的FsImage中。
(3) 如何fencing: NameNode每次寫(xiě)Editlog都需要傳遞一個(gè)編號(hào)Epoch給JN,JN會(huì)對(duì)比Epoch,如果比自己保存的Epoch大或相同,則可以寫(xiě),JN更新自己的Epoch到最新,否則拒絕操作。在切換時(shí),Standby轉(zhuǎn)換為Active時(shí),會(huì)把Epoch+1,這樣就防止即使之前的NameNode向JN寫(xiě)日志,也會(huì)失敗。
(4) 寫(xiě)日志:
(a) NN通過(guò)RPC向N個(gè)JN異步寫(xiě)Editlog,當(dāng)有N/2+1個(gè)寫(xiě)成功,則本次寫(xiě)成功。
(b) 寫(xiě)失敗的JN下次不再寫(xiě),直到調(diào)用滾動(dòng)日志操作,若此時(shí)JN恢復(fù)正常,則繼續(xù)向其寫(xiě)日志。
(c) 每條editlog都有一個(gè)編號(hào)txid,NN寫(xiě)日志要保證txid是連續(xù)的,JN在接收寫(xiě)日志時(shí),會(huì)檢查txid是否與上次連續(xù),否則寫(xiě)失敗。
(5) 讀日志:
(a) 定期遍歷所有JN,獲取未消化的editlog,按照txid排序。
(b) 根據(jù)txid消化editlog。
(6) 切換時(shí)日志恢復(fù)機(jī)制
(a) 主從切換時(shí)觸發(fā)
(b) 準(zhǔn)備恢復(fù)(prepareRecovery),standby向JN發(fā)送RPC請(qǐng)求,獲取txid信息,并對(duì)選出最好的JN。
(c) 接受恢復(fù)(acceptRecovery),standby向JN發(fā)送RPC,JN之間同步Editlog日志。
(d) Finalized日志。即關(guān)閉當(dāng)前editlog輸出流時(shí)或滾動(dòng)日志時(shí)的操作。
(e) Standby同步editlog到最新
(7) 如何選取最好的JN
(a) 有Finalized的不用in-progress
(b) 多個(gè)Finalized的需要判斷txid是否相等
(c) 沒(méi)有Finalized的首先看誰(shuí)的epoch更大
(d) Epoch一樣則選txid大的。
DataNode 節(jié)點(diǎn):
共享 HDFS 的元數(shù)據(jù)信息以及共享HDFS 的數(shù)據(jù)塊和 DataNode 之間的映射關(guān)系。DataNode 會(huì)同時(shí)向主 NameNode 和備 NameNode 上報(bào)數(shù)據(jù)塊的位置信息。
NameNode主備切換過(guò)程
NameNode 主備切換主要由 ZKFailoverController、HealthMonitor 和 ActiveStandbyElector 這 3 個(gè)組件來(lái)協(xié)同實(shí)現(xiàn):
ZKFailoverController 作為 NameNode 機(jī)器上一個(gè)獨(dú)立的進(jìn)程啟動(dòng) (在 hdfs 啟動(dòng)腳本之中的進(jìn)程名為 zkfc),啟動(dòng)的時(shí)候會(huì)創(chuàng)建 HealthMonitor 和 ActiveStandbyElector 這兩個(gè)主要的內(nèi)部組件,ZKFailoverController 在創(chuàng)建 HealthMonitor 和 ActiveStandbyElector 的同時(shí),也會(huì)向 HealthMonitor 和 ActiveStandbyElector 注冊(cè)相應(yīng)的回調(diào)方法。
NameNode 實(shí)現(xiàn)主備切換的流程如圖所示,有以下幾步:

HealthMonitor 初始化完成后會(huì)啟動(dòng)內(nèi)部的線程來(lái)定時(shí)調(diào)用對(duì)應(yīng) NameNode 的 HAServiceProtocol RPC 接口的方法,對(duì) NameNode 的健康狀態(tài)進(jìn)行檢測(cè)。
HealthMonitor 如果檢測(cè)到 NameNode 的健康狀態(tài)發(fā)生變化,會(huì)回調(diào) ZKFailoverController 注冊(cè)的相應(yīng)方法進(jìn)行處理。
如果 ZKFailoverController 判斷需要進(jìn)行主備切換,會(huì)首先使用ActiveStandbyElector 來(lái)進(jìn)行自動(dòng)的主備選舉。
ActiveStandbyElector 與 Zookeeper 進(jìn)行交互完成自動(dòng)的主備選舉。
ActiveStandbyElector 在主備選舉完成后,會(huì)回調(diào) ZKFailoverController 的相應(yīng)方法來(lái)通知當(dāng)前的 NameNode 成為主 NameNode 或備 NameNode。
ZKFailoverController 調(diào)用對(duì)應(yīng) NameNode 的 HAServiceProtocol RPC 接口的方法將 NameNode 轉(zhuǎn)換為 Active 狀態(tài)或 Standby 狀態(tài)。
腦裂(split-brain)
腦裂(split-brain),指在一個(gè)高可用(HA)系統(tǒng)中,當(dāng)聯(lián)系著的兩個(gè)節(jié)點(diǎn)斷開(kāi)聯(lián)系時(shí),本來(lái)為一個(gè)整體的系統(tǒng),分裂為兩個(gè)獨(dú)立節(jié)點(diǎn),這時(shí)兩個(gè)節(jié)點(diǎn)開(kāi)始爭(zhēng)搶共享資源,結(jié)果會(huì)導(dǎo)致系統(tǒng)混亂,數(shù)據(jù)損壞。
腦裂問(wèn)題處理:
共享存儲(chǔ)的fencing,確保只有一個(gè)NN能寫(xiě)成功。使用QJM實(shí)現(xiàn)fencing,下文敘述原理。
DataNode的fencing,確保只有一個(gè)NN能命令DN。HDFS-1972中詳細(xì)描述了DN如何實(shí)現(xiàn)fencing。
(1) 每個(gè)NN改變狀態(tài)的時(shí)候,向DN發(fā)送自己的狀態(tài)和一個(gè)序列號(hào)。
(2) DN在運(yùn)行過(guò)程中維護(hù)此序列號(hào),當(dāng)主備切換時(shí),新的NN在返回DN心跳時(shí)會(huì)返回自己的active狀態(tài)和一個(gè)更大的序列號(hào)。DN接收到這個(gè)返回是認(rèn)為該NN為新的active。
(3) 如果這時(shí)原來(lái)的active(比如GC)恢復(fù),返回給DN的心跳信息包含active狀態(tài)和原來(lái)的序列號(hào),這時(shí)DN就會(huì)拒絕這個(gè)NN的命令。
此外HDFS-1972中還解決了一些有可能導(dǎo)致誤刪除block的隱患,在failover后,active在DN匯報(bào)所有刪除報(bào)告前不應(yīng)該刪除任何block。
客戶端fencing,確保只有一個(gè)NN能響應(yīng)客戶端請(qǐng)求。讓訪問(wèn)standby NN的客戶端直接失敗。在RPC層封裝了一層,通過(guò)FailoverProxyProvider以重試的方式連接NN。通過(guò)若干次連接一個(gè)NN失敗后嘗試連接新的NN,對(duì)客戶端的影響是重試的時(shí)候增加一定的延遲??蛻舳丝梢栽O(shè)置重試此時(shí)和時(shí)間。