腦裂的概念與成因
"split brain"原本是指醫(yī)學(xué)中的“裂腦綜合征”,即連接大腦左右半球的胼胝體受損到一定程度后發(fā)生的癥狀秧耗。左右腦分離后备籽,會分別處理知覺、形成概念和對刺激產(chǎn)生反應(yīng)分井,相當(dāng)于有兩個腦在一個身體運作车猬,會造成患者行為的沖突。例如:
- 當(dāng)一個裂腦患者更衣時尺锚,他有時會一只手將褲子拉起珠闰,卻另一只手將褲子往下脫。
- 當(dāng)一個影像只投射在裂腦患者的左視覺區(qū)瘫辩,他無法說出看見了什么——因為左視覺區(qū)的影像只會傳遞到右腦伏嗜,而大部分人的語音控制中心在左腦,患者的左右腦無法交流信息伐厌。
split brain這個詞也被計算機科學(xué)引入承绸,指采用主從(master-slave)架構(gòu)的分布式系統(tǒng)中,出現(xiàn)了多個活動的主節(jié)點的情況挣轨。但正常情況下军熏,集群中應(yīng)該只有一個活動主節(jié)點。
造成腦裂的原因主要是網(wǎng)絡(luò)分區(qū)(這個詞之前在講CAP理論時就已經(jīng)出現(xiàn)過了)卷扮。由于網(wǎng)絡(luò)故障或者集群節(jié)點之間的通信鏈路有問題荡澎,導(dǎo)致原本的一個集群被物理分割成為兩個甚至多個小的、獨立運作的集群画饥,這些小集群各自會選舉出自己的主節(jié)點衔瓮,并同時對外提供服務(wù)。網(wǎng)絡(luò)分區(qū)恢復(fù)后抖甘,這些小集群再度合并為一個集群热鞍,就出現(xiàn)了多個活動的主節(jié)點。
另外衔彻,主節(jié)點假死也有可能造成腦裂薇宠。由于當(dāng)前主節(jié)點暫時無響應(yīng)(如負載過高、頻繁GC等)導(dǎo)致其向其他節(jié)點發(fā)送心跳信號不及時艰额,其他節(jié)點認為它已經(jīng)宕機澄港,就觸發(fā)主節(jié)點的重新選舉。新的主節(jié)點選舉出來后柄沮,假死的主節(jié)點又復(fù)活回梧,就出現(xiàn)了兩個主節(jié)點废岂。
腦裂的危害非常大,會破壞集群數(shù)據(jù)和對外服務(wù)的一致性狱意,所以在各分布式系統(tǒng)的設(shè)計中湖苞,都會千方百計地避免產(chǎn)生腦裂。下面舉兩個例子說說详囤。
腦裂的避免方案
一般有以下三種思路來避免腦裂:
- 法定人數(shù)/多數(shù)機制(Quorum)
- 隔離機制(Fencing)
- 冗余通信機制(Redundant communication)
例1:ZooKeeper & Quorum
Quorum一詞的含義是“法定人數(shù)”财骨,在ZooKeeper的環(huán)境中,指的是ZK集群能夠正常對外提供服務(wù)所需要的最少有效節(jié)點數(shù)藏姐。也就是說隆箩,如果n個節(jié)點的ZK集群有少于m個節(jié)點是up的,那么整個集群就down了羔杨。m就是所謂Quorum size捌臊,并且:
m = n / 2 + 1
為什么是這個數(shù)呢?
考慮一個n = 5的ZK集群兜材,并且它按3:2分布在兩個機房中娃属。
假設(shè)m = 2(即n / 2),當(dāng)兩個機房之間的網(wǎng)絡(luò)中斷時护姆,Server 1~3和Server 4~5將分別形成獨立的集群矾端,并且都能對外提供服務(wù)——也就意味著都能重新選舉出各自的Leader,即產(chǎn)生了腦裂卵皂。當(dāng)網(wǎng)絡(luò)恢復(fù)秩铆,兩個集群合并時,它們的數(shù)據(jù)就會不一致灯变。
但是殴玛,若m = 3(即n / 2 + 1),那么網(wǎng)絡(luò)中斷后添祸,DC2上的兩個節(jié)點不滿足Quorum要求的數(shù)量滚粟,故只有DC1上的三個節(jié)點能選舉出Leader并提供服務(wù),DC2上的兩個節(jié)點不能提供服務(wù)刃泌,當(dāng)然也就不會破壞數(shù)據(jù)一致性了凡壤。
由上可知,ZK的Quorum機制其實就是要求集群中過半的節(jié)點是正常的耙替,所以ZK集群包含奇數(shù)個節(jié)點比偶數(shù)個節(jié)點要更好亚侠。顯然,如果集群有6個節(jié)點的話俗扇,Quorum size是4硝烂,即能夠容忍2個節(jié)點失敗,而5個節(jié)點的集群同樣能容忍2個節(jié)點失敗铜幽,所以可靠性是相同的滞谢。偶數(shù)節(jié)點還需要額外多管理一個節(jié)點串稀,不劃算。
上面說的是網(wǎng)絡(luò)分區(qū)的情況狮杨,如果是Leader假死呢厨诸?
之前某篇文章中其實說過了,集群每次選舉出一個Leader時禾酱,都會自增紀元值(epoch),也就是Leader的代數(shù)绘趋。所以颤陶,就算原來的Leader復(fù)活,它的紀元值已經(jīng)小于新選舉出來的現(xiàn)任Leader的紀元值陷遮,F(xiàn)ollower就會拒絕所有舊Leader發(fā)來的請求滓走,所以不會產(chǎn)生腦裂。當(dāng)然帽馋,有一部分Follower可能對新選舉出的Leader沒有感知搅方,但由于上述Quorum機制的保證,這部分肯定不會占多數(shù)绽族,故集群能夠正常運轉(zhuǎn)姨涡。除ZK外,Kafka集群的Controller也是靠紀元值防止腦裂的吧慢。
例2:HDFS NameNode HA & Fencing
下面先貼出HDFS高可用的官方經(jīng)典架構(gòu)圖涛漂。
HDFS NameNode高可用需要兩個NN節(jié)點,一個處于活動狀態(tài)检诗,另一個處于熱備狀態(tài)匈仗,由ZKFailoverController組件借助外部ZK集群提供主備切換支持。
當(dāng)活動NN假死時逢慌,ZK集群長時間收不到心跳信號悠轩,就會觸發(fā)熱備NN提升為活動NN,之前的NN復(fù)活就造成腦裂攻泼。如何解決呢火架?答案就是隔離,即將原來那個假死又復(fù)活的NN限制起來(就像用籬笆圍起來一樣)忙菠,使其無法對外提供服務(wù)距潘。具體來講涉及到三方面。
- 兩個NN中同時只有一個能向共享存儲(QJM方案下就是JournalNode集群)寫入edit log只搁;
- 兩個NN中同時只有一個能向DataNode發(fā)出數(shù)據(jù)增刪的指令音比;
- 兩個NN中同時只有一個能響應(yīng)客戶端的請求。
為了實現(xiàn)Fencing氢惋,成為活動NN的節(jié)點會在ZK中創(chuàng)建一個路徑為/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb
的持久znode洞翩。當(dāng)正常發(fā)生主備切換時稽犁,ZK Session正常關(guān)閉的同時會一起刪除上述znode。但是骚亿,如果NN假死已亥,ZK Session異常關(guān)閉,/hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb
這個znode就會殘留下來来屠。由熱備升格為活動的NN會檢測到這個節(jié)點虑椎,并執(zhí)行Fencing邏輯:
嘗試調(diào)用舊活動NN的RPC接口中的相關(guān)方法,強制將其轉(zhuǎn)換成熱備狀態(tài)俱笛;
如果轉(zhuǎn)換失敗捆姜,那么就根據(jù)
dfs.ha.fencing.methods
執(zhí)行sshfence、shellfence兩種隔離措施迎膜。sshfence就是通過SSH登錄到該節(jié)點上泥技,執(zhí)行fuser命令通過定位端口號殺掉NameNode進程;shellfence就是執(zhí)行用戶定義的Shell腳本來隔離NameNode進程磕仅。
只有Fencing執(zhí)行完畢之后珊豹,新的NN才會真正轉(zhuǎn)換成活動狀態(tài)并提供服務(wù),所以能夠避免腦裂榕订。
最后廢話一句店茶,JournalNode集群區(qū)分新舊NN同樣是靠紀元值,而它的可用性也是靠Quorum機制——即如果JournalNode集群有2N + 1個節(jié)點的話劫恒,最多可以容忍N個節(jié)點失敗忽妒。
The End
冗余通信機制沒有提到,其實就是在節(jié)點之間添加額外的心跳線兼贸,防止一個心跳路徑斷開導(dǎo)致誤判段直。
帝都疫情開始反彈,還是老實在家待著吧溶诞。
民那周末快樂鸯檬,晚安。