HDFS 2.0 的 HA 實(shí)現(xiàn)
- Active NameNode 和 Standby NameNode:兩臺(tái) NameNode 形成互備,一臺(tái)處于 Active 狀態(tài)吏够,為主 NameNode勾给,另外一臺(tái)處于 Standby 狀態(tài),為備 NameNode锅知,只有主 NameNode 才能對(duì)外提供讀寫服務(wù)播急。
- 主備切換控制器 ZKFailoverController:ZKFailoverController 作為獨(dú)立的進(jìn)程運(yùn)行,對(duì) NameNode 的主備切換進(jìn)行總體控制售睹。ZKFailoverController 能及時(shí)檢測(cè)到 NameNode 的健康狀況桩警,在主 NameNode 故障時(shí)借助 Zookeeper 實(shí)現(xiàn)自動(dòng)的主備選舉和切換,當(dāng)然 NameNode 目前也支持不依賴于 Zookeeper 的手動(dòng)主備切換昌妹。
- Zookeeper 集群:為主備切換控制器提供主備選舉支持捶枢。
- 共享存儲(chǔ)系統(tǒng):共享存儲(chǔ)系統(tǒng)是實(shí)現(xiàn) NameNode 的高可用最為關(guān)鍵的部分,共享存儲(chǔ)系統(tǒng)保存了 NameNode 在運(yùn)行過程中所產(chǎn)生的 HDFS 的元數(shù)據(jù)飞崖。主 NameNode 和備NameNode 通過共享存儲(chǔ)系統(tǒng)實(shí)現(xiàn)元數(shù)據(jù)同步烂叔。在進(jìn)行主備切換的時(shí)候,新的主 NameNode 在確認(rèn)元數(shù)據(jù)完全同步之后才能繼續(xù)對(duì)外提供服務(wù)固歪。
- DataNode 節(jié)點(diǎn):除了通過共享存儲(chǔ)系統(tǒng)共享 HDFS 的元數(shù)據(jù)信息之外蒜鸡,主 NameNode 和備 NameNode 還需要共享 HDFS 的數(shù)據(jù)塊和 DataNode 之間的映射關(guān)系胯努。DataNode 會(huì)同時(shí)向主 NameNode 和備 NameNode 上報(bào)數(shù)據(jù)塊的位置信息。
NameNode 主備切換主要由 ZKFailoverController逢防、HealthMonitor 和 ActiveStandbyElector 這 3 個(gè)組件來協(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)方法灰署。
- HealthMonitor 主要負(fù)責(zé)檢測(cè) NameNode 的健康狀態(tài),如果檢測(cè)到 NameNode 的狀態(tài)發(fā)生變化辜伟,會(huì)回調(diào) ZKFailoverController 的相應(yīng)方法進(jìn)行自動(dòng)的主備選舉氓侧。
- ActiveStandbyElector 主要負(fù)責(zé)完成自動(dòng)的主備選舉,內(nèi)部封裝了 Zookeeper 的處理邏輯导狡,一旦 Zookeeper 主備選舉完成约巷,會(huì)回調(diào) ZKFailoverController 的相應(yīng)方法來進(jìn)行 NameNode 的主備狀態(tài)切換。
NameNode 實(shí)現(xiàn)主備切換有以下幾步:
- HealthMonitor 初始化完成之后會(huì)啟動(dòng)內(nè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 來進(jìn)行自動(dòng)的主備選舉。
- ActiveStandbyElector 與 Zookeeper 進(jìn)行交互完成自動(dòng)的主備選舉贫橙。
- ActiveStandbyElector 在主備選舉完成后贪婉,會(huì)回調(diào) ZKFailoverController 的相應(yīng)方法來通知當(dāng)前的 NameNode 成為主 NameNode 或備 NameNode。
-
ZKFailoverController 調(diào)用對(duì)應(yīng) NameNode 的 HAServiceProtocol RPC 接口的方法將 NameNode 轉(zhuǎn)換為 Active 狀態(tài)或 Standby 狀態(tài)卢肃。
主備切換
防止腦裂
Zookeeper 在工程實(shí)踐的過程中經(jīng)常會(huì)發(fā)生的一個(gè)現(xiàn)象就是 Zookeeper 客戶端“假死”疲迂,所謂的“假死”是指如果 Zookeeper 客戶端機(jī)器負(fù)載過高或者正在進(jìn)行 JVM Full GC,那么可能會(huì)導(dǎo)致 Zookeeper 客戶端到 Zookeeper 服務(wù)端的心跳不能正常發(fā)出莫湘,一旦這個(gè)時(shí)間持續(xù)較長尤蒿,超過了配置的 Zookeeper Session Timeout 參數(shù)的話,Zookeeper 服務(wù)端就會(huì)認(rèn)為客戶端的 session 已經(jīng)過期從而將客戶端的 Session 關(guān)閉幅垮⊙兀“假死”有可能引起分布式系統(tǒng)常說的雙主或腦裂 (brain-split) 現(xiàn)象。具體到本文所述的 NameNode忙芒,假設(shè) NameNode1 當(dāng)前為 Active 狀態(tài)示弓,NameNode2 當(dāng)前為 Standby 狀態(tài)。如果某一時(shí)刻 NameNode1 對(duì)應(yīng)的 ZKFailoverController 進(jìn)程發(fā)生了“假死”現(xiàn)象呵萨,那么 Zookeeper 服務(wù)端會(huì)認(rèn)為 NameNode1 掛掉了奏属,根據(jù)前面的主備切換邏輯,NameNode2 會(huì)替代 NameNode1 進(jìn)入 Active 狀態(tài)甘桑。但是此時(shí) NameNode1 可能仍然處于 Active 狀態(tài)正常運(yùn)行拍皮,即使隨后 NameNode1 對(duì)應(yīng)的 ZKFailoverController 因?yàn)樨?fù)載下降或者 Full GC 結(jié)束而恢復(fù)了正常,感知到自己和 Zookeeper 的 Session 已經(jīng)關(guān)閉跑杭,但是由于網(wǎng)絡(luò)的延遲以及 CPU 線程調(diào)度的不確定性铆帽,仍然有可能會(huì)在接下來的一段時(shí)間窗口內(nèi) NameNode1 認(rèn)為自己還是處于 Active 狀態(tài)。這樣 NameNode1 和 NameNode2 都處于 Active 狀態(tài)德谅,都可以對(duì)外提供服務(wù)爹橱。這種情況對(duì)于 NameNode 這類對(duì)數(shù)據(jù)一致性要求非常高的系統(tǒng)來說是災(zāi)難性的,數(shù)據(jù)會(huì)發(fā)生錯(cuò)亂且無法恢復(fù)窄做。Zookeeper 社區(qū)對(duì)這種問題的解決方法叫做 fencing愧驱,中文翻譯為隔離,也就是想辦法把舊的 Active NameNode 隔離起來椭盏,使它不能正常對(duì)外提供服務(wù)组砚。
ActiveStandbyElector 為了實(shí)現(xiàn) fencing,會(huì)在成功創(chuàng)建 Zookeeper 節(jié)點(diǎn)
hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock
從而成為 Active NameNode 之后掏颊,創(chuàng)建另外一個(gè)路徑為/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb
的持久節(jié)點(diǎn)糟红,這個(gè)節(jié)點(diǎn)里面保存了這個(gè) Active NameNode 的地址信息。Active NameNode 的 ActiveStandbyElector 在正常的狀態(tài)下關(guān)閉 Zookeeper Session 的時(shí)候 (注意由于/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock
是臨時(shí)節(jié)點(diǎn)乌叶,也會(huì)隨之刪除)盆偿,會(huì)一起刪除節(jié)點(diǎn)/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb
。但是如果 ActiveStandbyElector 在異常的狀態(tài)下 Zookeeper Session 關(guān)閉 (比如前述的 Zookeeper 假死)准浴,那么由于/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb
是持久節(jié)點(diǎn)事扭,會(huì)一直保留下來。后面當(dāng)另一個(gè) NameNode 選主成功之后乐横,會(huì)注意到上一個(gè) Active NameNode 遺留下來的這個(gè)節(jié)點(diǎn)求橄,從而會(huì)回調(diào) ZKFailoverController 的方法對(duì)舊的 Active NameNode 進(jìn)行 fencing。
基于 QJM 的共享存儲(chǔ)系統(tǒng)的總體架構(gòu)
基于 QJM 的共享存儲(chǔ)系統(tǒng)主要用于保存 EditLog晰奖,并不保存 FSImage 文件谈撒。FSImage 文件還是在 NameNode 的本地磁盤上。QJM 共享存儲(chǔ)的基本思想來自于 Paxos 算法匾南,采用多個(gè)稱為 JournalNode 的節(jié)點(diǎn)組成的 JournalNode 集群來存儲(chǔ) EditLog啃匿。每個(gè) JournalNode 保存同樣的 EditLog 副本。每次 NameNode 寫 EditLog 的時(shí)候蛆楞,除了向本地磁盤寫入 EditLog 之外溯乒,也會(huì)并行地向 JournalNode 集群之中的每一個(gè) JournalNode 發(fā)送寫請(qǐng)求,只要大多數(shù) (majority) 的 JournalNode 節(jié)點(diǎn)返回成功就認(rèn)為向 JournalNode 集群寫入 EditLog 成功豹爹。如果有 2N+1 臺(tái) JournalNode裆悄,那么根據(jù)大多數(shù)的原則,最多可以容忍有 N 臺(tái) JournalNode 節(jié)點(diǎn)掛掉臂聋。
Active NameNode 和 StandbyNameNode 使用 JouranlNode 集群來進(jìn)行數(shù)據(jù)同步的過程如圖所示光稼,Active NameNode 首先把 EditLog 提交到 JournalNode 集群或南,然后 Standby NameNode 再從 JournalNode 集群定時(shí)同步 EditLog:
數(shù)據(jù)恢復(fù)機(jī)制分析
處于 Standby 狀態(tài)的 NameNode 轉(zhuǎn)換為 Active 狀態(tài)的時(shí)候,有可能上一個(gè) Active NameNode 發(fā)生了異常退出艾君,那么 JournalNode 集群中各個(gè) JournalNode 上的 EditLog 就可能會(huì)處于不一致的狀態(tài)采够,所以首先要做的事情就是讓 JournalNode 集群中各個(gè)節(jié)點(diǎn)上的 EditLog 恢復(fù)為一致。另外如前所述冰垄,當(dāng)前處于 Standby 狀態(tài)的 NameNode 的內(nèi)存中的文件系統(tǒng)鏡像有很大的可能是落后于舊的 Active NameNode 的蹬癌,所以在 JournalNode 集群中各個(gè)節(jié)點(diǎn)上的 EditLog 達(dá)成一致之后,接下來要做的事情就是從 JournalNode 集群上補(bǔ)齊落后的 EditLog虹茶。只有在這兩步完成之后逝薪,當(dāng)前新的 Active NameNode 才能安全地對(duì)外提供服務(wù)。
HDFS 2.0 Federation 實(shí)現(xiàn)
在 1.0 中蝴罪,HDFS 的架構(gòu)設(shè)計(jì)有以下缺點(diǎn):
- namespace 擴(kuò)展性差:在單一的 NN 情況下董济,因?yàn)樗?namespace 數(shù)據(jù)都需要加載到內(nèi)存,所以物理機(jī)內(nèi)存的大小限制了整個(gè) HDFS 能夠容納文件的最大個(gè)數(shù)(namespace 指的是 HDFS 中樹形目錄和文件結(jié)構(gòu)以及文件對(duì)應(yīng)的 block 信息)要门;
- 性能可擴(kuò)展性差:由于所有請(qǐng)求都需要經(jīng)過 NN感局,單一 NN 導(dǎo)致所有請(qǐng)求都由一臺(tái)機(jī)器進(jìn)行處理,很容易達(dá)到單臺(tái)機(jī)器的吞吐暂衡;
- 隔離性差:多租戶的情況下询微,單一 NN 的架構(gòu)無法在租戶間進(jìn)行隔離,會(huì)造成不可避免的相互影響狂巢。
而 Federation 的設(shè)計(jì)就是為了解決這些問題撑毛,采用 Federation 的最主要原因是設(shè)計(jì)實(shí)現(xiàn)簡單,而且還能解決問題唧领。
Federation 的核心設(shè)計(jì)思想
Federation 的核心思想是將一個(gè)大的 namespace 劃分多個(gè)子 namespace藻雌,并且每個(gè) namespace 分別由單獨(dú)的 NameNode 負(fù)責(zé),這些 NameNode 之間互相獨(dú)立斩个,不會(huì)影響胯杭,不需要做任何協(xié)調(diào)工作(其實(shí)跟拆集群有一些相似),集群的所有 DataNode 會(huì)被多個(gè) NameNode 共享受啥。
其中做个,每個(gè)子 namespace 和 DataNode 之間會(huì)由數(shù)據(jù)塊管理層作為中介建立映射關(guān)系。