Zookeeper集羣腦裂問題

關於集羣中的"腦裂"問題,之前已經在這裏詳細介紹過,下面重點說下Zookeeper腦裂問題的處理辦法。ooKeeper是用來協調(同步)分佈式進程的服務,提供了一個簡單高性能的協調內核,用戶可以在此之上構建更多複雜的分佈式協調功能。腦裂通常會出現在集羣環境中,比如ElasticSearch、Zookeeper集羣,而這些集羣環境有一個統一的特點,就是它們有一個大腦,比如ElasticSearch集羣中有Master節點,Zookeeper集羣中有Leader節點。

一、 Zookeeper 集羣節點爲什麼要部署成奇數
zookeeper容錯指的是:當宕掉幾個zookeeper節點服務器之後,剩下的個數必須大於宕掉的個數,也就是剩下的節點服務數必須大於n/2,這樣zookeeper集羣纔可以繼續使用,無論奇偶數都可以選舉leader。例如5臺zookeeper節點機器最多宕掉2臺,還可以繼續使用,因爲剩下3臺大於5/2。至於爲什麼最好爲奇數個節點?這樣是爲了以最大容錯服務器個數的條件下,能節省資源。比如,最大容錯爲2的情況下,對應的zookeeper服務數,奇數爲5,而偶數爲6,也就是6個zookeeper服務的情況下最多能宕掉2個服務,所以從節約資源的角度看,沒必要部署6(偶數)個zookeeper服務節點。

zookeeper集羣有這樣一個特性:集羣中只要有過半的機器是正常工作的,那麼整個集羣對外就是可用的。也就是說如果有2個zookeeper節點,那麼只要有1個zookeeper節點死了,那麼zookeeper服務就不能用了,因爲1沒有過半,所以2個zookeeper的死亡容忍度爲0;同理,要是有3個zookeeper,一個死了,還剩下2個正常的,過半了,所以3個zookeeper的容忍度爲1;同理也可以多列舉幾個:2->0; 3->1; 4->1; 5->2; 6->2 就會發現一個規律,2n和2n-1的容忍度是一樣的,都是n-1,所以爲了更加高效,何必增加那一個不必要的zookeeper呢。所以說,根據以上可以得出結論:從資源節省的角度來考慮,zookeeper集羣的節點最好要部署成奇數個!

二、 Zookeeper 集羣中的"腦裂"場景說明
對於一個集羣,想要提高這個集羣的可用性,通常會採用多機房部署,比如現在有一個由6臺zkServer所組成的一個集羣,部署在了兩個機房:

正常情況下,此集羣只會有一個Leader,那麼如果機房之間的網絡斷了之後,兩個機房內的zkServer還是可以相互通信的,如果不考慮過半機制,那麼就會出現每個機房內部都將選出一個Leader。

這就相當於原本一個集羣,被分成了兩個集羣,出現了兩個"大腦",這就是所謂的"腦裂"現象。對於這種情況,其實也可以看出來,原本應該是統一的一個集羣對外提供服務的,現在變成了兩個集羣同時對外提供服務,如果過了一會,斷了的網絡突然聯通了,那麼此時就會出現問題了,兩個集羣剛剛都對外提供服務了,數據該怎麼合併,數據衝突怎麼解決等等問題。剛剛在說明腦裂場景時有一個前提條件就是沒有考慮過半機制,所以實際上Zookeeper集羣中是不會輕易出現腦裂問題的,原因在於過半機制。

zookeeper的過半機制:在領導者選舉的過程中,如果某臺zkServer獲得了超過半數的選票,則此zkServer就可以成爲Leader了。舉個簡單的例子:如果現在集羣中有5臺zkServer,那麼half=5/2=2,那麼也就是說,領導者選舉的過程中至少要有三臺zkServer投了同一個zkServer,纔會符合過半機制,才能選出來一個Leader。

那麼zookeeper選舉的過程中爲什麼一定要有一個過半機制驗證?
因爲這樣不需要等待所有zkServer都投了同一個zkServer就可以選舉出來一個Leader了,這樣比較快,所以叫快速領導者選舉算法。

zookeeper過半機制中爲什麼是大於,而不是大於等於?這就是更腦裂問題有關係了,比如回到上文出現腦裂問題的場景 [如上圖1]:當機房中間的網絡斷掉之後,機房1內的三臺服務器會進行領導者選舉,但是此時過半機制的條件是 "節點數 > 3",也就是說至少要4臺zkServer才能選出來一個Leader,所以對於機房1來說它不能選出一個Leader,同樣機房2也不能選出一個Leader,這種情況下整個集羣當機房間的網絡斷掉後,整個集羣將沒有Leader。而如果過半機制的條件是 "節點數 >= 3",那麼機房1和機房2都會選出一個Leader,這樣就出現了腦裂。這就可以解釋爲什麼過半機制中是大於而不是大於等於,目的就是爲了防止腦裂。

如果假設我們現在只有5臺機器,也部署在兩個機房:

此時過半機制的條件是 "節點數 > 2",也就是至少要3臺服務器才能選出一個Leader,此時機房件的網絡斷開了,對於機房1來說是沒有影響的,Leader依然還是Leader,對於機房2來說是選不出來Leader的,此時整個集羣中只有一個Leader。因此總結得出,有了過半機制,對於一個Zookeeper集羣來說,要麼沒有Leader,要麼只有1個Leader,這樣zookeeper也就能避免了腦裂問題。

三、 Zookeeper 集羣"腦裂"問題處理
1.  什麼是腦裂?
簡單點來說,腦裂(Split-Brain) 就是比如當你的 cluster 裏面有兩個節點,它們都知道在這個 cluster 裏需要選舉出一個 master。那麼當它們兩個之間的通信完全沒有問題的時候,就會達成共識,選出其中一個作爲 master。但是如果它們之間的通信出了問題,那麼兩個結點都會覺得現在沒有 master,所以每個都把自己選舉成 master,於是 cluster 裏面就會有兩個 master。

對於Zookeeper來說有一個很重要的問題,就是到底是根據一個什麼樣的情況來判斷一個節點死亡down掉了?在分佈式系統中這些都是有監控者來判斷的,但是監控者也很難判定其他的節點的狀態,唯一一個可靠的途徑就是心跳,Zookeeper也是使用心跳來判斷客戶端是否仍然活着。

使用ZooKeeper來做Leader HA基本都是同樣的方式:每個節點都嘗試註冊一個象徵leader的臨時節點,其他沒有註冊成功的則成爲follower,並且通過watch機制 (這裏有介紹) 監控着leader所創建的臨時節點,Zookeeper通過內部心跳機制來確定leader的狀態,一旦leader出現意外Zookeeper能很快獲悉並且通知其他的follower,其他flower在之後作出相關反應,這樣就完成了一個切換,這種模式也是比較通用的模式,基本大部分都是這樣實現的。但是這裏面有個很嚴重的問題,如果注意不到會導致短暫的時間內系統出現腦裂,因爲心跳出現超時可能是leader掛了,但是也可能是zookeeper節點之間網絡出現了問題,導致leader假死的情況,leader其實並未死掉,但是與ZooKeeper之間的網絡出現問題導致Zookeeper認爲其掛掉瞭然後通知其他節點進行切換,這樣follower中就有一個成爲了leader,但是原本的leader並未死掉,這時候client也獲得leader切換的消息,但是仍然會有一些延時,zookeeper需要通訊需要一個一個通知,這時候整個系統就很混亂可能有一部分client已經通知到了連接到新的leader上去了,有的client仍然連接在老的leader上,如果同時有兩個client需要對leader的同一個數據更新,並且剛好這兩個client此刻分別連接在新老的leader上,就會出現很嚴重問題。

這裏做下小總結:
假死:由於心跳超時(網絡原因導致的)認爲leader死了,但其實leader還存活着。
腦裂:由於假死會發起新的leader選舉,選舉出一個新的leader,但舊的leader網絡又通了,導致出現了兩個leader ,有的客戶端連接到老的leader,而有的客戶端則連接到新的leader。

2.  zookeeper腦裂是什麼原因導致的?
主要原因是Zookeeper集羣和Zookeeper client判斷超時並不能做到完全同步,也就是說可能一前一後,如果是集羣先於client發現,那就會出現上面的情況。同時,在發現並切換後通知各個客戶端也有先後快慢。一般出現這種情況的機率很小,需要leader節點與Zookeeper集羣網絡斷開,但是與其他集羣角色之間的網絡沒有問題,還要滿足上面那些情況,但是一旦出現就會引起很嚴重的後果,數據不一致。

3.  zookeeper是如何解決"腦裂"問題的?
要解決Split-Brain腦裂的問題,一般有下面幾種種方法:
Quorums (法定人數) 方式: 比如3個節點的集羣,Quorums = 2, 也就是說集羣可以容忍1個節點失效,這時候還能選舉出1個lead,集羣還可用。比如4個節點的集羣,它的Quorums = 3,Quorums要超過3,相當於集羣的容忍度還是1,如果2個節點失效,那麼整個集羣還是無效的。這是zookeeper防止"腦裂"默認採用的方法。
採用Redundant communications (冗餘通信)方式:集羣中採用多種通信方式,防止一種通信方式失效導致集羣中的節點無法通信。
Fencing (共享資源) 方式:比如能看到共享資源就表示在集羣中,能夠獲得共享資源的鎖的就是Leader,看不到共享資源的,就不在集羣中。
仲裁機制方式。
啓動磁盤鎖定方式。

要想避免zookeeper"腦裂"情況其實也很簡單,在follower節點切換的時候不在檢查到老的leader節點出現問題後馬上切換,而是在休眠一段足夠的時間,確保老的leader已經獲知變更並且做了相關的shutdown清理工作了然後再註冊成爲master就能避免這類問題了,這個休眠時間一般定義爲與zookeeper定義的超時時間就夠了,但是這段時間內系統可能是不可用的,但是相對於數據不一致的後果來說還是值得的。

1、zooKeeper默認採用了Quorums這種方式來防止"腦裂"現象。即只有集羣中超過半數節點投票才能選舉出Leader。這樣的方式可以確保leader的唯一性,要麼選出唯一的一個leader,要麼選舉失敗。在zookeeper中Quorums作用如下:
1]  集羣中最少的節點數用來選舉leader保證集羣可用。
2] 通知客戶端數據已經安全保存前集羣中最少數量的節點數已經保存了該數據。一旦這些節點保存了該數據,客戶端將被通知已經安全保存了,可以繼續其他任務。而集羣中剩餘的節點將會最終也保存了該數據。

假設某個leader假死,其餘的followers選舉出了一個新的leader。這時,舊的leader復活並且仍然認爲自己是leader,這個時候它向其他followers發出寫請求也是會被拒絕的。因爲每當新leader產生時,會生成一個epoch標號(標識當前屬於那個leader的統治時期),這個epoch是遞增的,followers如果確認了新的leader存在,知道其epoch,就會拒絕epoch小於現任leader epoch的所有請求。那有沒有follower不知道新的leader存在呢,有可能,但肯定不是大多數,否則新leader無法產生。Zookeeper的寫也遵循quorum機制,因此,得不到大多數支持的寫是無效的,舊leader即使各種認爲自己是leader,依然沒有什麼作用。

zookeeper除了可以採用上面默認的Quorums方式來避免出現"腦裂",還可以可採用下面的預防措施:
2、添加冗餘的心跳線,例如雙線條線,儘量減少“裂腦”發生機會。
3、啓用磁盤鎖。正在服務一方鎖住共享磁盤,"裂腦"發生時,讓對方完全"搶不走"共享磁盤資源。但使用鎖磁盤也會有一個不小的問題,如果佔用共享盤的一方不主動"解鎖",另一方就永遠得不到共享磁盤。現實中假如服務節點突然死機或崩潰,就不可能執行解鎖命令。後備節點也就接管不了共享資源和應用服務。於是有人在HA中設計了"智能"鎖。即正在服務的一方只在發現心跳線全部斷開(察覺不到對端)時才啓用磁盤鎖。平時就不上鎖了。
4、設置仲裁機制。例如設置參考IP(如網關IP),當心跳線完全斷開時,2個節點都各自ping一下 參考IP,不通則表明斷點就出在本端,不僅"心跳"、還兼對外"服務"的本端網絡鏈路斷了,即使啓動(或繼續)應用服務也沒有用了,那就主動放棄競爭,讓能夠ping通參考IP的一端去起服務。更保險一些,ping不通參考IP的一方乾脆就自我重啓,以徹底釋放有可能還佔用着的那些共享資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章