Zookeeper和分散式環境中的假死腦裂問題(轉發)
阿新 • • 發佈:2019-02-18
最近和同事聊天無意間發現他們的系統也存在腦裂的問題。想想當初在我們的系統中為了解決腦裂花了非常大的功夫,現在和大家一起討論下腦裂,假死等等這些問題和解決的方法。
在一個大叢集中往往會有一個master存在,在長期執行過程中不可避免的會出現宕機等問題導致master不可用,在出現這樣的情況以後往往會對系統產生很大的影響,所以一般的分散式叢集中的master都採用了高可用的解決方案來避免這樣的情況發生。
master-slaver方式,存在一個master節點,平時對外服務,同時有一個slaver節點,監控著master,同時有某種方式來進行資料的同步。如果在master掛掉以後slaver能很快獲知並迅速切換成為新的master。在以往master-slaver的監控切換是個很大的難題,但是現在有了Zookeeper的話能比較優雅的解決這一類問題。
master-slaver結構
master-slaver實現起來非常簡單,而且在master上面的各種操作效率要較其他HA解決方案要高,早期的時候監控和切換很難控制,但是後來zookeeper出現了,他的watch和分散式鎖機制很好的解決了這一類問題。
我們的系統和同事的系統都是這種模式,但是後來都發現由於ZooKeeper使用上的問題存在腦裂的問題。
記得很久以前參加一個大牛的技術交流會他就提到過在叢集中假死問題是一個非常讓人頭痛的問題,假死也是導致腦裂的根源。
根據一個什麼樣的情況能判斷一個節點死亡了down掉了,人可能很容易判斷,但是對於在分散式系統中這些是有監控者來判斷的,對於監控者來說很難判定其他的節點的狀態,唯一可靠點途徑的就是心跳,包括ZooKeeper就是使用心跳來判斷客戶端是否仍然活著的,使用ZooKeeper來做master HA基本都是同樣的方式,每個節點都嘗試註冊一個象徵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上,就會出現很嚴重問題。
出現這種情況的主要原因在與Zookeeper叢集和Zookeeperclient判斷超時並不能做到完全同步(這些還依賴於作業系統排程等,很難保證),也就是說可能一前一後,如果是叢集先於client發現那就會出現上面的情況了。同時在發現並切換後通知各個客戶端也有先後快慢。出現這種情況的機率很小,需要master與zookeeper叢集網路斷開但是與其他叢集角色之間的網路沒有問題,還要滿足上面那些條件,但是一旦出現就會引發很嚴重的後果,資料不一致了。
避免這種情況其實也很簡單,在slaver切換的時候不在檢查到老的master出現問題後馬上切換,而是在休眠一段足夠的時間,確保老的master已經獲知變更並且做了相關的shutdown清理工作了然後再註冊成為master就能避免這類問題了,這個休眠時間一般定義為與zookeeper定義的超時時間就夠了,但是這段時間內系統可能是不可用的,但是相對於資料不一致的後果我想還是值得的。
當然最徹底的解決這類問題的方案是將master HA叢集做成peer2peer的,遮蔽掉外部Zookeeper的依賴。每個節點都是對等的沒有主次,這樣就不會存在腦裂的問題,但是這種ha解決方案需要使用兩階段,paxos這類資料一致性保證協議來實現,不可避免的會降低系統資料變更的系統,如果系統中主要是對master的讀取操作很少更新就很適合了。