前言:web
RDS系統致力於MySQL數據的高可用,高可靠,高性能以及在線擴展功能,實現這些特性的主要邏輯功能都運行在管理服務器上,一旦管理服務器宕機,數據庫的在線擴展功能/備份功能/故障恢復功能等都無從談起。然而,以前RDS系統管理服務器倒是單點服務,爲了保證整個系統的穩定性,管理服務器須要實現高可用,結合當前主流的高可用方案,決定使用Zookeeper來實現服務的高可用。數據庫
基本設計方案原理:服務器
以下圖所示,管理服務器A B C會在zk的root節點上註冊臨時序列節點/root/manager000000001 /root/manager000000002 /root/manager0000000003,序列最小的節點會被選爲leader對外提供服務,其餘節點做爲熱備節點隨時準備升爲leader。在此圖中,A是leader,B C是standby。一旦A服務器由於某些緣由宕機,zk就會將該服務器註冊的臨時節點移除掉,而後通知全部其餘節點B C,B C會選出序列號最小的節點做爲新的leader對外提供服務,此時B就會被選爲新主。網絡
血案現場:session
會有這麼一種比較特殊的場景須要考慮,好比當前leader是A BC都是備機。假如A和zk集羣之間的網絡出現了異常,A會收到一個鏈接狀態被持久化爲Disconnected的event,可是ZK Server並無在這時移除A註冊的臨時節點,因此理論上A仍是leader直至session timeout,session timeout後zk會將A註冊的臨時節點移除掉,而後通知B C選出新的leader,顯而易見,B由於序列號小會成爲新的leader。可是問題來了,session timeout的時候A的客戶端並無接收到任何notification,換句話說,它依然會認爲本身是leader,這個時候就出現了這樣的場景,A認爲本身是leader,而B一樣會認爲本身是leader,即同時出現兩個leader對外提供服務的狀況。這很顯然是不合理的,但如何深刻地理解並解決這個問題呢?性能
我的認爲理解並解決這個問題須要理解下面三個子問題:spa
1. 理解Zookeeper中Session的含義以及Connection Loss和Session Expired的關係設計
2. 理解Zookeeper中Session爲何由Server維護,而不禁Client維護3d
3. 理解做爲leader的A在整個流程中應該如何轉變本身的角色,來避免腦裂orm
對zookeeper中Connection Loss和Session Expired的理解
Session是指當Client建立一個同Server的鏈接時產生的會話。鏈接Connected以後Session狀態就開啓,Zookeeper服務器和Client採用長鏈接方式(Client會不停地向Server發送心跳)保證session在不出現網絡問題、服務器宕機或Client宕機狀況下能夠一直存在。所以,在正常狀況下,session會一直有效,而且ZK集羣上全部機器都會保存這個Session信息。
在ZK中,不少數據和狀態都是和會話綁定的,一旦會話失效,那麼ZK就開始清除和這個會話有關的信息,包括這個會話建立的臨時節點和註冊的全部Watcher。
一旦網絡鏈接由於某種緣由斷開或者zk集羣發生宕機,ZK Client會立刻捕獲到這個異常,封裝爲一個ConnectionLoss的事件,而後啓動自動重連機制在地址列表中選擇新的地址進行重連。重連會有三種結果:
(1)在session timeout時間內重連成功,client會從新收到一個syncconnected的event,並將鏈接從新持久化爲connected狀態
(2)超過session timeout時間段後重連成功,client會收到一個expired的event,並將鏈接持久化爲closed狀態
(3)一直重連不上,client將不會收到任何event
很顯然,不管重連成功與否,在session timeout那個重要的時間點,ZK Client是接收不到任何ZK Server清理臨時節點的信息的。這也就致使ZK會通知了B C節點A已經再也不是Leader,A自身卻沒有接收到這樣的信息,依舊對外提供服務,進而產生腦裂的問題。
Zookeeper中Session爲何由Server維護,而不禁Client維護
可能不少朋友看了上面的討論就會想爲何ZK Client不維護Session信息,若是這樣作了,ZK Client就會在Session Timeout時獲得相應的通知。
好,如今假如這樣實現了,看看會發生什麼。設想有這麼一種真實場景,某個鏈接的Session Timeout是15s,ZK集羣由於未知緣由發生宕機,5min以後集羣恢復成功。在Session Timeout時,ZK Client確實能夠知道Session失效,而後作降主操做,可是ZK Server殊不知道Session已經失效,也就不會通知其餘節點選出新的Leader,此時整個系統實際上處於沒有Leader的狀態。即便5min以後重連成功,由於舊Session對應的臨時節點沒有被清理且序號最小,ZK依然會認爲Leader是該臨時節點,而實際上該臨時節點對應不到任何的ZK Client,因此這種狀況下系統依然選不出Leader。
可見,若是由Client維護Session,在某些場景下(網絡異常或者集羣宕機時間超過Session Timeout),因爲邏輯問題根本選不出Leader。
所以這種方案是不可行的。
那能不能從應用層面避免腦裂問題呢?帶着問題進入下個部分。
避免腦裂問題:做爲leader的A在整個流程中應該如何轉變本身的角色
由於ZK自己的設計使得這種場景下沒有一個完美的解決方案,能夠考慮採用退化的方案進行處理。
A在接收到DisConnected事件後就降主,不對外提供服務。而後等待接下來的發生的事情,首先可能發生的事件是在Session Timeout時間段內重連成功獲得SyncConnected事件,這時A能夠從新升級爲主,對外提供服務。若是這段時間內沒有重連成功,ZK Server在Session Timeout時會將A註冊的臨時節點移除,並通知B和C A已經中止對外服務了,須要選出新的leader。由於A本身已經降主了,因此在選出新leader後也不會出現多主現象。若是A在Session Timeout時間段外重連又成功了,那此時確定新的leader已經選出來了,A須要從新註冊做爲新的備機候選。
可使用以下的流程圖解釋這個過程:
這種應用層面的方案在必定程度上解決了腦裂問題,可是會出現一段時間系統無Leader的狀況,持續時間最長爲Session超時時間。
總結:
結合上述分析,能夠針對Session Timeout的問題得出一個應用層面的解決方案,即Client分階段轉變角色方案。以下圖所示,箭頭上方紅色標示表示Client所處不一樣階段,箭頭下方藍色標示表示Client在不一樣階段的角色轉變。
本文章爲做者原創
🈲禁止🈲
其餘公衆帳號轉載,如有轉載,請標明出處