這是分布式系統中一個很實際的問題,書上說的不是很詳細,整理總結一下。
1、腦裂和假死
1.1 腦裂
官方定義:當一個集群的不同部分在同一時間都認為自己是活動的時候,我們就可以將這個現象稱為腦裂症狀。通俗的說,就是比如當你的 cluster 里面有兩個結點,它們都知道在這個 cluster 里需要選舉出一個 master。那么當它們兩之間的通信完全沒有問題的時候,就會達成共識,選出其中一個作為 master。但是如果它們之間的通信出了問題,那么兩個結點都會覺得現在沒有 master,所以每個都把自己選舉成 master。於是 cluster 里面就會有兩個 master。舉例:
UserA和UserB分別將自己的信息注冊在RouterA和RouterB中。RouterA和RouterB使用數據同步(2PC),來同步信息。那么當UserA想要向UserB發送一個消息的時候,需要現在RouterA中查詢出UserA到UserB的消息路由路徑,然后再交付給相應的路徑進行路由。
當腦裂發生的時候,相當RouterA和RouterB直接的聯系丟失了,RouterA認為整個系統中只有它一個Router,RouterB也是這樣認為的。那么相當於RouterA中沒有UserB的信息,RouterB中沒有UserA的信息了,此時UserA再發送消息給UserB的時候,RouterA會認為UserB已經離線了,然后將該信息進行離線持久化,這樣整個網絡的路由是不是就亂掉了。
對於Zookeeper來說有一個很重要的問題,就是到底是根據一個什么樣的情況來判斷一個節點死亡down掉了。 在分布式系統中這些都是有監控者來判斷的,但是監控者也很難判定其他的節點的狀態,唯一一個可靠的途徑就是心跳,Zookeeper也是使用心跳來判斷客戶端是否仍然活着,但是使用心跳機制來判斷節點的存活狀態也帶來了假死問題。
1.2 假死
ZooKeeper每個節點都嘗試注冊一個象征master的臨時節點,其他沒有注冊成功的則成為slaver,並且通過watch機制監控着master所創建的臨時節點,Zookeeper通過內部心跳機制來確定master的狀態,一旦master出現意外Zookeeper能很快獲悉並且通知其他的slaver,其他slaver在之后作出相關反應。這樣就完成了一個切換。
這種模式也是比較通用的模式,基本大部分都是這樣實現的,但是這里面有個很嚴重的問題,如果注意不到會導致短暫的時間內系統出現腦裂,因為心跳出現超時可能是master掛了,但是也可能是master,zookeeper之間網絡出現了問題,也同樣可能導致。這種情況就是假死,master並未死掉,但是與ZooKeeper之間的網絡出現問題導致Zookeeper認為其掛掉了然后通知其他節點進行切換,這樣slaver中就有一個成為了master,但是原本的master並未死掉,這時候client也獲得master切換的消息,但是仍然會有一些延時,zookeeper需要通訊需要一個一個通知,這時候整個系統就很混亂可能有一部分client已經通知到了連接到新的master上去了,有的client仍然連接在老的master上如果同時有兩個client需要對master的同一個數據更新並且剛好這兩個client此刻分別連接在新老的master上,就會出現很嚴重問題。
1.3 總結
假死:由於心跳超時(網絡原因導致的)認為master死了,但其實master還存活着。
腦裂:由於假死會發起新的master選舉,選舉出一個新的master,但舊的master網絡又通了,導致出現了兩個master ,有的客戶端連接到老的master 有的客戶端鏈接到新的master。
2、Zookeeper的解決方案
要解決Split-Brain的問題,一般有3種方式:
Quorums(ˈkwôrəm 法定人數) :比如3個節點的集群,Quorums = 2, 也就是說集群可以容忍1個節點失效,這時候還能選舉出1個lead,集群還可用。比如4個節點的集群,它的Quorums = 3,Quorums要超過3,相當於集群的容忍度還是1,如果2個節點失效,那么整個集群還是無效的
Redundant communications:冗余通信的方式,集群中采用多種通信方式,防止一種通信方式失效導致集群中的節點無法通信。
Fencing, 共享資源的方式:比如能看到共享資源就表示在集群中,能夠獲得共享資源的鎖的就是Leader,看不到共享資源的,就不在集群中。
ZooKeeper默認采用了Quorums這種方式,即只有集群中超過半數節點投票才能選舉出Leader。這樣的方式可以確保leader的唯一性,要么選出唯一的一個leader,要么選舉失敗。在ZooKeeper中Quorums有2個作用:
集群中最少的節點數用來選舉Leader保證集群可用:通知客戶端數據已經安全保存前集群中最少數量的節點數已經保存了該數據。一旦這些節點保存了該數據,客戶端將被通知已經安全保存了,可以繼續其他任務。而集群中剩余的節點將會最終也保存了該數據。
假設某個leader假死,其余的followers選舉出了一個新的leader。這時,舊的leader復活並且仍然認為自己是leader,這個時候它向其他followers發出寫請求也是會被拒絕的。因為每當新leader產生時,會生成一個epoch,這個epoch是遞增的,followers如果確認了新的leader存在,知道其epoch,就會拒絕epoch小於現任leader epoch的所有請求。那有沒有follower不知道新的leader存在呢,有可能,但肯定不是大多數,否則新leader無法產生。Zookeeper的寫也遵循quorum機制,因此,得不到大多數支持的寫是無效的,舊leader即使各種認為自己是leader,依然沒有什么作用。
3、總結
總結一下就是,通過Quorums機制來防止腦裂和假死,當leader掛掉之后,可以重新選舉出新的leader節點使整個集群達成一致;當出現假死現象時,通過epoch大小來拒絕舊的leader發起的請求,在前面也已經講到過,這個時候,重新恢復通信的老的leader節點會進入恢復模式,與新的leader節點做數據同步,perfect。