Zookeeper集群脑裂问题
關(guān)于集群中的"腦裂"問題,之前已經(jīng)在這里詳細(xì)介紹過,下面重點(diǎn)說下Zookeeper腦裂問題的處理辦法。ooKeeper是用來協(xié)調(diào)(同步)分布式進(jìn)程的服務(wù),提供了一個(gè)簡單高性能的協(xié)調(diào)內(nèi)核,用戶可以在此之上構(gòu)建更多復(fù)雜的分布式協(xié)調(diào)功能。腦裂通常會(huì)出現(xiàn)在集群環(huán)境中,比如ElasticSearch、Zookeeper集群,而這些集群環(huán)境有一個(gè)統(tǒng)一的特點(diǎn),就是它們有一個(gè)大腦,比如ElasticSearch集群中有Master節(jié)點(diǎn),Zookeeper集群中有Leader節(jié)點(diǎn)。
一、 Zookeeper 集群節(jié)點(diǎn)為什么要部署成奇數(shù)
zookeeper容錯(cuò)指的是:當(dāng)宕掉幾個(gè)zookeeper節(jié)點(diǎn)服務(wù)器之后,剩下的個(gè)數(shù)必須大于宕掉的個(gè)數(shù),也就是剩下的節(jié)點(diǎn)服務(wù)數(shù)必須大于n/2,這樣zookeeper集群才可以繼續(xù)使用,無論奇偶數(shù)都可以選舉leader。例如5臺(tái)zookeeper節(jié)點(diǎn)機(jī)器最多宕掉2臺(tái),還可以繼續(xù)使用,因?yàn)槭O?臺(tái)大于5/2。至于為什么最好為奇數(shù)個(gè)節(jié)點(diǎn)?這樣是為了以最大容錯(cuò)服務(wù)器個(gè)數(shù)的條件下,能節(jié)省資源。比如,最大容錯(cuò)為2的情況下,對應(yīng)的zookeeper服務(wù)數(shù),奇數(shù)為5,而偶數(shù)為6,也就是6個(gè)zookeeper服務(wù)的情況下最多能宕掉2個(gè)服務(wù),所以從節(jié)約資源的角度看,沒必要部署6(偶數(shù))個(gè)zookeeper服務(wù)節(jié)點(diǎn)。
zookeeper集群有這樣一個(gè)特性:集群中只要有過半的機(jī)器是正常工作的,那么整個(gè)集群對外就是可用的。也就是說如果有2個(gè)zookeeper節(jié)點(diǎn),那么只要有1個(gè)zookeeper節(jié)點(diǎn)死了,那么zookeeper服務(wù)就不能用了,因?yàn)?沒有過半,所以2個(gè)zookeeper的死亡容忍度為0;同理,要是有3個(gè)zookeeper,一個(gè)死了,還剩下2個(gè)正常的,過半了,所以3個(gè)zookeeper的容忍度為1;同理也可以多列舉幾個(gè):2->0; 3->1; 4->1; 5->2; 6->2 就會(huì)發(fā)現(xiàn)一個(gè)規(guī)律,2n和2n-1的容忍度是一樣的,都是n-1,所以為了更加高效,何必增加那一個(gè)不必要的zookeeper呢。所以說,根據(jù)以上可以得出結(jié)論:從資源節(jié)省的角度來考慮,zookeeper集群的節(jié)點(diǎn)最好要部署成奇數(shù)個(gè)!
二、 Zookeeper 集群中的"腦裂"場景說明
對于一個(gè)集群,想要提高這個(gè)集群的可用性,通常會(huì)采用多機(jī)房部署,比如現(xiàn)在有一個(gè)由6臺(tái)zkServer所組成的一個(gè)集群,部署在了兩個(gè)機(jī)房:
正常情況下,此集群只會(huì)有一個(gè)Leader,那么如果機(jī)房之間的網(wǎng)絡(luò)斷了之后,兩個(gè)機(jī)房內(nèi)的zkServer還是可以相互通信的,如果不考慮過半機(jī)制,那么就會(huì)出現(xiàn)每個(gè)機(jī)房內(nèi)部都將選出一個(gè)Leader。
這就相當(dāng)于原本一個(gè)集群,被分成了兩個(gè)集群,出現(xiàn)了兩個(gè)"大腦",這就是所謂的"腦裂"現(xiàn)象。對于這種情況,其實(shí)也可以看出來,原本應(yīng)該是統(tǒng)一的一個(gè)集群對外提供服務(wù)的,現(xiàn)在變成了兩個(gè)集群同時(shí)對外提供服務(wù),如果過了一會(huì),斷了的網(wǎng)絡(luò)突然聯(lián)通了,那么此時(shí)就會(huì)出現(xiàn)問題了,兩個(gè)集群剛剛都對外提供服務(wù)了,數(shù)據(jù)該怎么合并,數(shù)據(jù)沖突怎么解決等等問題。剛剛在說明腦裂場景時(shí)有一個(gè)前提條件就是沒有考慮過半機(jī)制,所以實(shí)際上Zookeeper集群中是不會(huì)輕易出現(xiàn)腦裂問題的,原因在于過半機(jī)制。
zookeeper的過半機(jī)制:在領(lǐng)導(dǎo)者選舉的過程中,如果某臺(tái)zkServer獲得了超過半數(shù)的選票,則此zkServer就可以成為Leader了。舉個(gè)簡單的例子:如果現(xiàn)在集群中有5臺(tái)zkServer,那么half=5/2=2,那么也就是說,領(lǐng)導(dǎo)者選舉的過程中至少要有三臺(tái)zkServer投了同一個(gè)zkServer,才會(huì)符合過半機(jī)制,才能選出來一個(gè)Leader。
那么zookeeper選舉的過程中為什么一定要有一個(gè)過半機(jī)制驗(yàn)證?
因?yàn)檫@樣不需要等待所有zkServer都投了同一個(gè)zkServer就可以選舉出來一個(gè)Leader了,這樣比較快,所以叫快速領(lǐng)導(dǎo)者選舉算法。
zookeeper過半機(jī)制中為什么是大于,而不是大于等于?這就是更腦裂問題有關(guān)系了,比如回到上文出現(xiàn)腦裂問題的場景?[如上圖1]:當(dāng)機(jī)房中間的網(wǎng)絡(luò)斷掉之后,機(jī)房1內(nèi)的三臺(tái)服務(wù)器會(huì)進(jìn)行領(lǐng)導(dǎo)者選舉,但是此時(shí)過半機(jī)制的條件是 "節(jié)點(diǎn)數(shù) > 3",也就是說至少要4臺(tái)zkServer才能選出來一個(gè)Leader,所以對于機(jī)房1來說它不能選出一個(gè)Leader,同樣機(jī)房2也不能選出一個(gè)Leader,這種情況下整個(gè)集群當(dāng)機(jī)房間的網(wǎng)絡(luò)斷掉后,整個(gè)集群將沒有Leader。而如果過半機(jī)制的條件是 "節(jié)點(diǎn)數(shù) >= 3",那么機(jī)房1和機(jī)房2都會(huì)選出一個(gè)Leader,這樣就出現(xiàn)了腦裂。這就可以解釋為什么過半機(jī)制中是大于而不是大于等于,目的就是為了防止腦裂。
如果假設(shè)我們現(xiàn)在只有5臺(tái)機(jī)器,也部署在兩個(gè)機(jī)房:
此時(shí)過半機(jī)制的條件是 "節(jié)點(diǎn)數(shù) > 2",也就是至少要3臺(tái)服務(wù)器才能選出一個(gè)Leader,此時(shí)機(jī)房件的網(wǎng)絡(luò)斷開了,對于機(jī)房1來說是沒有影響的,Leader依然還是Leader,對于機(jī)房2來說是選不出來Leader的,此時(shí)整個(gè)集群中只有一個(gè)Leader。因此總結(jié)得出,有了過半機(jī)制,對于一個(gè)Zookeeper集群來說,要么沒有Leader,要么只有1個(gè)Leader,這樣zookeeper也就能避免了腦裂問題。
三、 Zookeeper 集群"腦裂"問題處理
1.? 什么是腦裂?
簡單點(diǎn)來說,腦裂(Split-Brain) 就是比如當(dāng)你的 cluster 里面有兩個(gè)節(jié)點(diǎn),它們都知道在這個(gè) cluster 里需要選舉出一個(gè) master。那么當(dāng)它們兩個(gè)之間的通信完全沒有問題的時(shí)候,就會(huì)達(dá)成共識(shí),選出其中一個(gè)作為 master。但是如果它們之間的通信出了問題,那么兩個(gè)結(jié)點(diǎn)都會(huì)覺得現(xiàn)在沒有 master,所以每個(gè)都把自己選舉成 master,于是 cluster 里面就會(huì)有兩個(gè) master。
對于Zookeeper來說有一個(gè)很重要的問題,就是到底是根據(jù)一個(gè)什么樣的情況來判斷一個(gè)節(jié)點(diǎn)死亡down掉了?在分布式系統(tǒng)中這些都是有監(jiān)控者來判斷的,但是監(jiān)控者也很難判定其他的節(jié)點(diǎn)的狀態(tài),唯一一個(gè)可靠的途徑就是心跳,Zookeeper也是使用心跳來判斷客戶端是否仍然活著。
使用ZooKeeper來做Leader HA基本都是同樣的方式:每個(gè)節(jié)點(diǎn)都嘗試注冊一個(gè)象征leader的臨時(shí)節(jié)點(diǎn),其他沒有注冊成功的則成為follower,并且通過watch機(jī)制 (這里有介紹) 監(jiān)控著leader所創(chuàng)建的臨時(shí)節(jié)點(diǎn),Zookeeper通過內(nèi)部心跳機(jī)制來確定leader的狀態(tài),一旦leader出現(xiàn)意外Zookeeper能很快獲悉并且通知其他的follower,其他flower在之后作出相關(guān)反應(yīng),這樣就完成了一個(gè)切換,這種模式也是比較通用的模式,基本大部分都是這樣實(shí)現(xiàn)的。但是這里面有個(gè)很嚴(yán)重的問題,如果注意不到會(huì)導(dǎo)致短暫的時(shí)間內(nèi)系統(tǒng)出現(xiàn)腦裂,因?yàn)樾奶霈F(xiàn)超時(shí)可能是leader掛了,但是也可能是zookeeper節(jié)點(diǎn)之間網(wǎng)絡(luò)出現(xiàn)了問題,導(dǎo)致leader假死的情況,leader其實(shí)并未死掉,但是與ZooKeeper之間的網(wǎng)絡(luò)出現(xiàn)問題導(dǎo)致Zookeeper認(rèn)為其掛掉了然后通知其他節(jié)點(diǎn)進(jìn)行切換,這樣follower中就有一個(gè)成為了leader,但是原本的leader并未死掉,這時(shí)候client也獲得leader切換的消息,但是仍然會(huì)有一些延時(shí),zookeeper需要通訊需要一個(gè)一個(gè)通知,這時(shí)候整個(gè)系統(tǒng)就很混亂可能有一部分client已經(jīng)通知到了連接到新的leader上去了,有的client仍然連接在老的leader上,如果同時(shí)有兩個(gè)client需要對leader的同一個(gè)數(shù)據(jù)更新,并且剛好這兩個(gè)client此刻分別連接在新老的leader上,就會(huì)出現(xiàn)很嚴(yán)重問題。
這里做下小總結(jié):
假死:由于心跳超時(shí)(網(wǎng)絡(luò)原因?qū)е碌?#xff09;認(rèn)為leader死了,但其實(shí)leader還存活著。
腦裂:由于假死會(huì)發(fā)起新的leader選舉,選舉出一個(gè)新的leader,但舊的leader網(wǎng)絡(luò)又通了,導(dǎo)致出現(xiàn)了兩個(gè)leader ,有的客戶端連接到老的leader,而有的客戶端則連接到新的leader。
2.? zookeeper腦裂是什么原因?qū)е碌?#xff1f;
主要原因是Zookeeper集群和Zookeeper client判斷超時(shí)并不能做到完全同步,也就是說可能一前一后,如果是集群先于client發(fā)現(xiàn),那就會(huì)出現(xiàn)上面的情況。同時(shí),在發(fā)現(xiàn)并切換后通知各個(gè)客戶端也有先后快慢。一般出現(xiàn)這種情況的幾率很小,需要leader節(jié)點(diǎn)與Zookeeper集群網(wǎng)絡(luò)斷開,但是與其他集群角色之間的網(wǎng)絡(luò)沒有問題,還要滿足上面那些情況,但是一旦出現(xiàn)就會(huì)引起很嚴(yán)重的后果,數(shù)據(jù)不一致。
3.? zookeeper是如何解決"腦裂"問題的?
要解決Split-Brain腦裂的問題,一般有下面幾種種方法:
Quorums (法定人數(shù)) 方式: 比如3個(gè)節(jié)點(diǎn)的集群,Quorums = 2, 也就是說集群可以容忍1個(gè)節(jié)點(diǎn)失效,這時(shí)候還能選舉出1個(gè)lead,集群還可用。比如4個(gè)節(jié)點(diǎn)的集群,它的Quorums = 3,Quorums要超過3,相當(dāng)于集群的容忍度還是1,如果2個(gè)節(jié)點(diǎn)失效,那么整個(gè)集群還是無效的。這是zookeeper防止"腦裂"默認(rèn)采用的方法。
采用Redundant communications (冗余通信)方式:集群中采用多種通信方式,防止一種通信方式失效導(dǎo)致集群中的節(jié)點(diǎn)無法通信。
Fencing (共享資源) 方式:比如能看到共享資源就表示在集群中,能夠獲得共享資源的鎖的就是Leader,看不到共享資源的,就不在集群中。
仲裁機(jī)制方式。
啟動(dòng)磁盤鎖定方式。
要想避免zookeeper"腦裂"情況其實(shí)也很簡單,在follower節(jié)點(diǎn)切換的時(shí)候不在檢查到老的leader節(jié)點(diǎn)出現(xiàn)問題后馬上切換,而是在休眠一段足夠的時(shí)間,確保老的leader已經(jīng)獲知變更并且做了相關(guān)的shutdown清理工作了然后再注冊成為master就能避免這類問題了,這個(gè)休眠時(shí)間一般定義為與zookeeper定義的超時(shí)時(shí)間就夠了,但是這段時(shí)間內(nèi)系統(tǒng)可能是不可用的,但是相對于數(shù)據(jù)不一致的后果來說還是值得的。
1、zooKeeper默認(rèn)采用了Quorums這種方式來防止"腦裂"現(xiàn)象。即只有集群中超過半數(shù)節(jié)點(diǎn)投票才能選舉出Leader。這樣的方式可以確保leader的唯一性,要么選出唯一的一個(gè)leader,要么選舉失敗。在zookeeper中Quorums作用如下:
1]??集群中最少的節(jié)點(diǎn)數(shù)用來選舉leader保證集群可用。
2]?通知客戶端數(shù)據(jù)已經(jīng)安全保存前集群中最少數(shù)量的節(jié)點(diǎn)數(shù)已經(jīng)保存了該數(shù)據(jù)。一旦這些節(jié)點(diǎn)保存了該數(shù)據(jù),客戶端將被通知已經(jīng)安全保存了,可以繼續(xù)其他任務(wù)。而集群中剩余的節(jié)點(diǎn)將會(huì)最終也保存了該數(shù)據(jù)。
假設(shè)某個(gè)leader假死,其余的followers選舉出了一個(gè)新的leader。這時(shí),舊的leader復(fù)活并且仍然認(rèn)為自己是leader,這個(gè)時(shí)候它向其他followers發(fā)出寫請求也是會(huì)被拒絕的。因?yàn)槊慨?dāng)新leader產(chǎn)生時(shí),會(huì)生成一個(gè)epoch標(biāo)號(hào)(標(biāo)識(shí)當(dāng)前屬于那個(gè)leader的統(tǒng)治時(shí)期),這個(gè)epoch是遞增的,followers如果確認(rèn)了新的leader存在,知道其epoch,就會(huì)拒絕epoch小于現(xiàn)任leader epoch的所有請求。那有沒有follower不知道新的leader存在呢,有可能,但肯定不是大多數(shù),否則新leader無法產(chǎn)生。Zookeeper的寫也遵循quorum機(jī)制,因此,得不到大多數(shù)支持的寫是無效的,舊leader即使各種認(rèn)為自己是leader,依然沒有什么作用。
zookeeper除了可以采用上面默認(rèn)的Quorums方式來避免出現(xiàn)"腦裂",還可以可采用下面的預(yù)防措施:
2、添加冗余的心跳線,例如雙線條線,盡量減少“裂腦”發(fā)生機(jī)會(huì)。
3、啟用磁盤鎖。正在服務(wù)一方鎖住共享磁盤,"裂腦"發(fā)生時(shí),讓對方完全"搶不走"共享磁盤資源。但使用鎖磁盤也會(huì)有一個(gè)不小的問題,如果占用共享盤的一方不主動(dòng)"解鎖",另一方就永遠(yuǎn)得不到共享磁盤。現(xiàn)實(shí)中假如服務(wù)節(jié)點(diǎn)突然死機(jī)或崩潰,就不可能執(zhí)行解鎖命令。后備節(jié)點(diǎn)也就接管不了共享資源和應(yīng)用服務(wù)。于是有人在HA中設(shè)計(jì)了"智能"鎖。即正在服務(wù)的一方只在發(fā)現(xiàn)心跳線全部斷開(察覺不到對端)時(shí)才啟用磁盤鎖。平時(shí)就不上鎖了。
4、設(shè)置仲裁機(jī)制。例如設(shè)置參考IP(如網(wǎng)關(guān)IP),當(dāng)心跳線完全斷開時(shí),2個(gè)節(jié)點(diǎn)都各自ping一下 參考IP,不通則表明斷點(diǎn)就出在本端,不僅"心跳"、還兼對外"服務(wù)"的本端網(wǎng)絡(luò)鏈路斷了,即使啟動(dòng)(或繼續(xù))應(yīng)用服務(wù)也沒有用了,那就主動(dòng)放棄競爭,讓能夠ping通參考IP的一端去起服務(wù)。更保險(xiǎn)一些,ping不通參考IP的一方干脆就自我重啟,以徹底釋放有可能還占用著的那些共享資源。
總結(jié)
以上是生活随笔為你收集整理的Zookeeper集群脑裂问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Resilience4j-轻量级熔断框架
- 下一篇: 整洁架构