Apache ZooKeeper - Leader 选举 如何保证分布式数据的一致性
文章目錄
- Pre
- 流程圖
- Leader 的協調過程
- ZK 是如何實現的
- 廣播模式
- 恢復模式
- 源碼實現
- 小結
Pre
Apache ZooKeeper - 選舉Leader源碼流程深度解析
在 ZooKeeper 集群中,服務器分為 Leader 服務器、 Follower 服務器以及 Observer 服務器。
我們可以這樣認為,Leader 選舉是一個過程,在這個過程中 ZooKeeper 主要做了兩個重要工作,一個是數據同步,另一個是選舉出新的 Leader 服務器。
今天我們繼續來看下 ZooKeeper 集群中的數據同步問題。
流程圖
Leader 的協調過程
CAP 定理是說一個分布式系統不能同時滿足一致性、可用性,以及分區容錯性。
今天要講的就是一致性。 其實 ZooKeeper 中實現的一致性也不是強一致性,即集群中各個服務器上的數據每時每刻都是保持一致的特性。在 ZooKeeper 中,采用的是最終一致的特性,即經過一段時間后,ZooKeeper 集群服務器上的數據最終保持一致的特性。
在 ZooKeeper 集群中,Leader 服務器主要負責處理事物性的請求,而在接收到一個客戶端的事務性請求操作時,Leader 服務器會先向集群中的各個機器針對該條會話發起投票詢問。
要想實現 ZooKeeper 集群中的最終一致性,我們先要確定什么情況下會對 ZooKeeper 集群服務產生不一致的情況。
在集群初始化啟動的時候,首先要同步集群中各個服務器上的數據。而在集群中 Leader 服務器崩潰時,需要選舉出新的 Leader 而在這一過程中會導致各個服務器上數據的不一致,所以當選舉出新的 Leader 服務器后需要進行數據的同步操作。
ZK 是如何實現的
ZooKeeper 在集群中采用的是多數原則方式,即當一個事務性的請求導致服務器上的數據發生改變時,ZooKeeper 只要保證集群上的多數機器的數據都正確變更了,就可以保證系統數據的一致性。
這是因為在一個 ZooKeeper 集群中,每一個 Follower 服務器都可以看作是 Leader 服務器的數據副本,需要保證集群中大多數機器數據是一致的,這樣在集群中出現個別機器故障的時候,ZooKeeper 集群依然能夠保證穩定運行。
在 ZooKeeper 集群服務的運行過程中,數據同步的過程如下圖所示。當執行完數據變更的會話請求時,需要對集群中的服務器進行數據同步。
廣播模式
ZooKeeper 在代碼層的實現中定義了一個 HashSet 類型的變量,用來管理在集群中的 Follower 服務器,之后調用 getForwardingFollowers 函數獲取在集群中的 Follower 服務器
public class Leader(){HashSet<LearnerHandler> forwardingFollowers;public List<LearnerHandler> getForwardingFollowers() {synchronized (forwardingFollowers) {return new ArrayList<LearnerHandler>(forwardingFollowers);}}在 ZooKeeper 集群服務器對一個事物性的請求操作進行投票并通過后,Leader 服務器執行isQuorumSynced 方法判斷該 ZooKeeper 集群中的 Follower 節點的連接狀態,由于 isQuorumSynced 方法可以被多個線程進行調用,所以在進行操作的時候要通過forwardingFollowers 字段進行加鎖操作。
之后遍歷集群中的 Follower 服務器,根據服務器 zxid、以及數據同步狀態等條件判斷服務器的執行邏輯是否成功。之后統計 Follower 服務器的 sid 并返回。
public boolean isQuorumSynced(QuorumVerifier qv) {synchronized (forwardingFollowers) {for (LearnerHandler learnerHandler: forwardingFollowers){if(learnerHandler.synced()){ids.add(learnerHandler.getSid());}}}}通過上面的介紹,Leader 服務器在集群中已經完成確定 Follower 服務器狀態等同步數據前的準備工作,
接下來 Leader 服務器會通過 request.setTxn 方法向集群中的 Follower 服務器發送數據變更的會話請求。
這個過程中,我們可以把 Leader 服務器看作是 ZooKeeper 服務中的客戶端,而其向集群中 Follower 服務器發送數據更新請求,集群中的 Follower 服務器收到請求后會處理該會話,之后進行數據變更操作。
如下面的代碼所示,在底層實現中,通過調用 request 請求對象的 setTxn 方法向 Follower 服務器發送請求,在 setTxn 函數中我們傳入的參數有操作類型字段 CONFIG_NODE,表明該操作是數據同步操作。
request.setTxn(new SetDataTxn(ZooDefs.CONFIG_NODE, request.qv.toString().getBytes(), -1));恢復模式
介紹完 Leader 節點如何管理 Follower 服務器進行數據同步后,接下來我們看一下當 Leader 服務器崩潰后 ZooKeeper 集群又是如何進行數據的恢復和同步的。
當 ZooKeeper 集群中一個 Leader 服務器失效時,會重新在 Follower 服務器中選舉出一個新的服務器作為 Leader 服務器。
而 ZooKeeper 服務往往處在高并發的使用場景中,如果在這個過程中有新的事務性請求操作,應該如何處理呢? 由于此時集群中不存在 Leader 服務器了,理論上 ZooKeeper 會直接丟失該條請求,會話不進行處理,但是這樣做在實際的生產中顯然是不行的,那么 ZooKeeper 具體是怎么做的呢?
在 ZooKeeper 中,重新選舉 Leader 服務器會經歷一段時間,因此理論上在 ZooKeeper 集群中會短暫的沒有 Leader 服務器,在這種情況下接收到事務性請求操作的時候,ZooKeeper 服務會先將這個會話進行掛起操作,掛起的會話不會計算會話的超時時間,之后在 Leader 服務器產生后系統會同步執行這些會話操作。
源碼實現
提到過一個 LearnerHandler 類, 當時我們只是簡單地從服務器之間的通信和協同工作的角度去分析了該類的作用。而 LearnerHandler 類其實可以看作是所有 Learner 服務器內部工作的處理者,它所負責的工作有:進行 Follower、Observer 服務器與 Leader 服務器的數據同步、事務性會話請求的轉發以及 Proposal 提議投票等功能。
LearnerHandler 是一個多線程的類,在 ZooKeeper 集群服務運行過程中,一個 Follower 或 Observer 服務器就對應一個 LearnerHandler 。在集群服務器彼此協調工作的過程中,Leader 服務器會與每一個 Learner 服務器維持一個長連接,并啟動一個單獨的 LearnerHandler 線程進行處理。
如下面的代碼所示,在 LearnerHandler 線程類中,最核心的方法就是 run 方法,處理數據同步等功能都在該方法中進行調用。首先通過 syncFollower 函數判斷數據同步的方式是否是快照方式。如果是快照方式,就將 Leader 服務器上的數據操作日志 dump 出來發送給 Follower 等服務器,在 Follower 等服務器接收到數據操作日志后,在本地執行該日志,最終完成數據的同步操作。
public void run() {boolean needSnap = syncFollower(peerLastZxid, leader.zk.getZKDatabase(), leader);if(needSnap){leader.zk.getZKDatabase().serializeSnapshot(oa);oa.writeString("BenWasHere", "signature");bufferedOutput.flush();}}小結
到這里我們就對 ZooKeeper 中數據一致性的解決原理和底層實現都做了較為詳細的介紹。我們總結一下,ZooKeeper 集群在處理一致性問題的時候基本采用了兩種方式來協調集群中的服務器工作,分別是恢復模式和廣播模式。
-
恢復模式:當 ZooKeeper 集群中的 Leader 服務器崩潰后,ZooKeeper 集群就采用恢復模式的方式進行工作,在這個工程中,ZooKeeper 集群會首先進行 Leader 節點服務器的重新選擇,之后在選舉出 Leader 服務器后對系統中所有的服務器進行數據同步進而保證集群中服務器上的數據的一致性。
-
廣播模式:當 ZooKeeper 集群中具有 Leader 服務器,并且可以正常工作時,集群中又有新的 Follower 服務器加入 ZooKeeper 中參與工作,這種情況常常發生在系統性能到達瓶頸,進而對系統進行動態擴容的使用場景。在這種情況下,如果不做任何操作,那么新加入的服務器作為 Follower 服務器,其上的數據與 ZooKeeper 集群中其他服務器上的數據不一致。當有新的查詢會話請求發送到 ZooKeeper 集群進行處理,而恰巧該請求實際被分發給這臺新加入的 Follower 機器進行處理,就會導致明明在集群中存在的數據,在這臺服務器上卻查詢不到,導致數據查詢不一致的情況。因此,在當有新的 Follower 服務器加入 ZooKeeper 集群中的時候,該臺服務器會在恢復模式下啟動,并找到集群中的 Leader 節點服務器,并同該 Leader 服務器進行數據同步。
走了~
總結
以上是生活随笔為你收集整理的Apache ZooKeeper - Leader 选举 如何保证分布式数据的一致性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apache ZooKeeper -
- 下一篇: Apache ZooKeeper - 集