Zookeeper可以幫我們實現服務的註冊與發現。然而,現在有一個問題是,如果只採用一個Zookeeper服務器,那麼當這個服務器宕機時,意味着整個分佈式服務無法正常工作。爲了解決這一問題,就需要Zookeeper集羣。
然而,在使用Zookeeper集羣時,也存在着一個問題,即集羣中數據一致性的維護。
如上圖所示,Zookeeper集羣是一主多從結構。
- 在更新數據時,首先更新到主服務節點,再更新到從服務節點;
- 在讀數據時,直接讀取任意從服務節點;
- 爲了保證從節點的數據一致性,Zookeeper採用ZAB協議,類似於一致性算法Paxos和Raft。
ZAB解決了集羣崩潰恢復和主從數據同步兩個問題。
1. 集羣崩潰恢復
服務節點共有三種狀態:
- Looking:選舉狀態;
- Following:從服務節點狀態;
- Leading:主服務節點狀態。
選舉過程
當主服務節點宕機時,從從服務節點中選舉主服務節點。
-
集羣中的服務節點各自向其他節點發起投票,投票當中包含自己的服務器ID和最大ZXID(自增事務ID);
-
節點會用自身的ZXID和從其他服務節點接收到的ZXID做比較。如果發現其他節點的ZXID比自己的大,也就是數據比自己新,那麼久重新發起投票,投票給目前已知的最大ZXID所屬的服務節點;
-
每次投票之後,服務器都會統計投票數量,判斷是否某個服務節點得到半數或以上的投票,如果存在這樣的節點,該節點將成爲準Leader,狀態變爲Leading,其他節點的狀態變爲Following。
發現階段
通過上述選舉階段後,爲了防止網絡等原因而發生的意外的情況,也就是說,可能出現多主節點的情況。這種情況會發生寫寫衝突,爲了避免這種情況,Leader集思廣益,接收所有的Follower發來的各自最新的epoch值,Leader從中選出最大的epoch,並基於此值加1,生成的新的epoch分發給各個Follower。
各個Follower接收到全新的epoch後,返回ACK給Leader,並帶上各自最大的ZXID和歷史事務日誌。Leader選出最大的ZXID,並更新自身的歷史日誌,以保持數據最新。
同步階段
把Leader收到的最新歷史事務日誌同步給集羣中所有的Follower,只有當半數Follower同步成功,這個準Leader才能成爲正式的Leader。
2. 主從數據同步
ZAB的數據寫入涉及到Broadcase階段,即Leader廣播到所有的Follower,其過程如下:
- 客戶端發出寫入數據請求給任意的Follower(服務註冊);
- Follower把寫入數據請求轉發給Leader(讀寫分離,讀節點不負責寫,只有主節點能夠寫入);
- Leader採用兩階段提交的方式,先發送Propose廣播給Follower(先保留提交日誌,後執行更新,類似於事務);
- Follower接收到Propose消息,寫入日誌成功後,返回ACK給Leader(開啓事務);
- Leader接收到半數以上的ACK消息,返回成功給客戶端,並且廣播Commit請求給Follower(提交事務,可以進行數據持久化)。
數據一致性有三種:強一致性、弱一致性和順序一致性。而Zookeeper屬於順序一致性。