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