Zookeeper和分布式環境中的假死腦裂問題
最近和同事聊天無意間發現他們的系統也存在腦裂的問題。想想當初在我們的系統中為了解決腦裂花了非常大的功夫,現在和大家一起討論下腦裂,假死等等這些問題和解決的方法。
在一個大集群中往往會有一個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的讀取操作很少更新就很適合了。
