Apache ZooKeeper - 选举Leader源码流程深度解析
文章目錄
- 流程圖
- Round
- Leader 選舉
- 服務(wù)啟動(dòng)時(shí)的 Leader 選舉
- 發(fā)起投票
- 接收投票
- 統(tǒng)計(jì)投票
- Leader 崩潰觸發(fā)的 Leader 選舉
- 變更服務(wù)器狀態(tài)
- 發(fā)起投票
- 接收投票
- 統(tǒng)計(jì)投票
- 源碼分析
流程圖
Round
Leader 選舉
Leader 服務(wù)器的作用是管理 ZooKeeper 集群中的其他服務(wù)器。
因此,如果是單獨(dú)一臺(tái)服務(wù)器,不構(gòu)成集群規(guī)模。在 ZooKeeper 服務(wù)的運(yùn)行中不會(huì)選舉 Leader 服務(wù)器,也不會(huì)作為 Leader 服務(wù)器運(yùn)行
我們知道一個(gè) ZooKeeper 服務(wù)要想滿足集群方式運(yùn)行,至少需要三臺(tái)服務(wù)器。這里我們就以三臺(tái)服務(wù)器組成的 ZooKeeper 集群為例,介紹一下 Leader 服務(wù)器選舉的內(nèi)部過(guò)程和底層實(shí)現(xiàn)。
Leader 服務(wù)器的選舉操作主要發(fā)生在兩種情況下:
- 第一種就是 ZooKeeper 集群服務(wù)啟動(dòng)的時(shí)候
- 第二種就是在 ZooKeeper 集群中舊的 Leader 服務(wù)器失效時(shí),這時(shí) ZooKeeper 集群需要選舉出新的 Leader 服務(wù)器
服務(wù)啟動(dòng)時(shí)的 Leader 選舉
我們先來(lái)看下在 ZooKeeper 集群服務(wù)最初啟動(dòng)的時(shí)候,Leader 服務(wù)器是如何選舉的。
在 ZooKeeper 集群?jiǎn)?dòng)時(shí),需要在集群中的服務(wù)器之間確定一臺(tái) Leader 服務(wù)器。
當(dāng) ZooKeeper 集群中的三臺(tái)服務(wù)器啟動(dòng)之后,首先會(huì)進(jìn)行通信檢查,如果集群中的服務(wù)器之間能夠進(jìn)行通信。集群中的三臺(tái)機(jī)器開(kāi)始嘗試尋找集群中的 Leader 服務(wù)器并進(jìn)行數(shù)據(jù)同步等操作。
如果這時(shí)沒(méi)有搜索到 Leader 服務(wù)器,說(shuō)明集群中不存在 Leader 服務(wù)器。這時(shí) ZooKeeper 集群開(kāi)始發(fā)起 Leader 服務(wù)器選舉。
在整個(gè) ZooKeeper 集群中 Leader 選舉主要可以分為三大步驟分別是:發(fā)起投票、接收投票、統(tǒng)計(jì)投票。
發(fā)起投票
在 ZooKeeper 服務(wù)器集群初始化啟動(dòng)的時(shí)候,集群中的每一臺(tái)服務(wù)器都會(huì)將自己作為 Leader 服務(wù)器進(jìn)行投票。也就是每次投票時(shí),發(fā)送的服務(wù)器的 myid(服務(wù)器標(biāo)識(shí)符)和 ZXID (集群投票信息標(biāo)識(shí)符)等選票信息字段都指向本機(jī)服務(wù)器。
而一個(gè)投票信息就是通過(guò)這兩個(gè)字段組成的。以集群中三個(gè)服務(wù)器 Serverhost1、Serverhost2、Serverhost3 為例,三個(gè)服務(wù)器的投票內(nèi)容分別是:Severhost1 的投票是(1,0)、Serverhost2 服務(wù)器的投票是(2,0)、Serverhost3 服務(wù)器的投票是(3,0)。
接收投票
集群中各個(gè)服務(wù)器在發(fā)起投票的同時(shí),也通過(guò)網(wǎng)絡(luò)接收來(lái)自集群中其他服務(wù)器的投票信息。
在接收到網(wǎng)絡(luò)中的投票信息后,服務(wù)器內(nèi)部首先會(huì)判斷該條投票信息的有效性。檢查該條投票信息的時(shí)效性,是否是本輪最新的投票,并檢查該條投票信息是否是處于 LOOKING 狀態(tài)的服務(wù)器發(fā)出的。
統(tǒng)計(jì)投票
在接收到投票后,ZooKeeper 集群就該處理和統(tǒng)計(jì)投票結(jié)果了。
對(duì)于每條接收到的投票信息,集群中的每一臺(tái)服務(wù)器都會(huì)將自己的投票信息與其接收到的 ZooKeeper 集群中的其他投票信息進(jìn)行對(duì)比。
主要進(jìn)行對(duì)比的內(nèi)容是 ZXID,ZXID 數(shù)值比較大的投票信息優(yōu)先作為 Leader 服務(wù)器。如果每個(gè)投票信息中的 ZXID 相同,就會(huì)接著比對(duì)投票信息中的 myid 信息字段,選舉出 myid 較大的服務(wù)器作為 Leader 服務(wù)器。
拿上面列舉的三個(gè)服務(wù)器組成的集群例子來(lái)說(shuō),對(duì)于 Serverhost1,服務(wù)器的投票信息是(1,0),該服務(wù)器接收到的 Serverhost2 服務(wù)器的投票信息是(2,0)。在 ZooKeeper 集群服務(wù)運(yùn)行的過(guò)程中,首先會(huì)對(duì)比 ZXID,發(fā)現(xiàn)結(jié)果相同之后,對(duì)比 myid,發(fā)現(xiàn) Serverhost2 服務(wù)器的 myid 比較大,于是更新自己的投票信息為(2,0),并重新向 ZooKeeper 集群中的服務(wù)器發(fā)送新的投票信息。
而 Serverhost2 服務(wù)器則保留自身的投票信息,并重新向 ZooKeeper 集群服務(wù)器中發(fā)送投票信息。
而當(dāng)每輪投票過(guò)后,ZooKeeper 服務(wù)都會(huì)統(tǒng)計(jì)集群中服務(wù)器的投票結(jié)果,判斷是否有過(guò)半數(shù)的機(jī)器投出一樣的信息。如果存在過(guò)半數(shù)投票信息指向的服務(wù)器,那么該臺(tái)服務(wù)器就被選舉為 Leader 服務(wù)器。
比如上面的例子中,ZooKeeper 集群會(huì)選舉 Severhost2 服務(wù)器作為 Leader 服務(wù)器。
當(dāng) ZooKeeper 集群選舉出 Leader 服務(wù)器后,ZooKeeper 集群中的服務(wù)器就開(kāi)始更新自己的角色信息,除被選舉成 Leader 的服務(wù)器之外,其他集群中的服務(wù)器角色變更為 Following。
Leader 崩潰觸發(fā)的 Leader 選舉
接下來(lái)我們?cè)倏匆幌略?ZooKeeper 集群服務(wù)的運(yùn)行過(guò)程中,Leader 服務(wù)器是如果進(jìn)行選舉的。
在 ZooKeeper 集群服務(wù)的運(yùn)行過(guò)程中,Leader 服務(wù)器作為處理事物性請(qǐng)求以及管理其他角色服務(wù)器,在 ZooKeeper 集群中起到關(guān)鍵的作用。
當(dāng) ZooKeeper 集群中的 Leader 服務(wù)器發(fā)生崩潰時(shí),集群會(huì)暫停處理事務(wù)性的會(huì)話請(qǐng)求,直到 ZooKeeper 集群中選舉出新的 Leader 服務(wù)器。
而整個(gè) ZooKeeper 集群在重新選舉 Leader 時(shí)也經(jīng)過(guò)了四個(gè)過(guò)程,分別是變更服務(wù)器狀態(tài)、發(fā)起投票、接收投票、統(tǒng)計(jì)投票。
其中,與初始化啟動(dòng)時(shí) Leader 服務(wù)器的選舉過(guò)程相比,變更狀態(tài)和發(fā)起投票這兩個(gè)階段的實(shí)現(xiàn)是不同的。下面我們來(lái)分別看看這兩個(gè)階段。
變更服務(wù)器狀態(tài)
與ZooKeeper 集群服務(wù)器初始化階段不同, 在 ZooKeeper 集群服務(wù)運(yùn)行的過(guò)程中,集群中每臺(tái)服務(wù)器的角色已經(jīng)確定了,當(dāng) Leader 服務(wù)器崩潰后 ,ZooKeeper 集群中的其他服務(wù)器會(huì)首先將自身的狀態(tài)信息變?yōu)?LOOKING 狀態(tài),該狀態(tài)表示服務(wù)器已經(jīng)做好選舉新 Leader 服務(wù)器的準(zhǔn)備了,這之后整個(gè) ZooKeeper 集群開(kāi)始進(jìn)入選舉新的 Leader 服務(wù)器過(guò)程。
發(fā)起投票
ZooKeeper 集群重新選舉 Leader 服務(wù)器的過(guò)程中發(fā)起投票的過(guò)程與初始化啟動(dòng)時(shí)發(fā)起投票的過(guò)程基本相同。首先每個(gè)集群中的服務(wù)器都會(huì)投票給自己,將投票信息中的 Zxid 和 myid 分別指向本機(jī)服務(wù)器。
接收投票
同服務(wù)啟動(dòng)時(shí)的 Leader 選舉的 接收投票步驟
統(tǒng)計(jì)投票
同服務(wù)啟動(dòng)時(shí)的 Leader 選舉的 統(tǒng)計(jì)投票步驟
源碼分析
ZooKeeper 中實(shí)現(xiàn)的選舉算法有三種,而在目前的 ZooKeeper 3.6 版本后,只支持 “快速選舉” 這一種算法。
而在代碼層面的實(shí)現(xiàn)中,QuorumCnxManager 作為核心的實(shí)現(xiàn)類(lèi),用來(lái)管理 Leader 服務(wù)器與 Follow 服務(wù)器的 TCP 通信,以及消息的接收與發(fā)送等功能。在 QuorumCnxManager 中,主要定義了 ConcurrentHashMap<Long, SendWorker> 類(lèi)型的 senderWorkerMap 數(shù)據(jù)字段,用來(lái)管理每一個(gè)通信的服務(wù)器。
public class QuorumCnxManager {final ConcurrentHashMap<Long, SendWorker> senderWorkerMap;final ConcurrentHashMap<Long, ArrayBlockingQueue<ByteBuffer>> queueSendMap;final ConcurrentHashMap<Long, ByteBuffer> lastMessageSent;}而在 QuorumCnxManager 類(lèi)的內(nèi)部,定義了 RecvWorker 內(nèi)部類(lèi)。該類(lèi)繼承了一個(gè) ZooKeeperThread 類(lèi)的多線程類(lèi)。主要負(fù)責(zé)消息接收。在 ZooKeeper 的實(shí)現(xiàn)中,為每一個(gè)集群中的通信服務(wù)器都分配一個(gè) RecvWorker,負(fù)責(zé)接收來(lái)自其他服務(wù)器發(fā)送的信息。在 RecvWorker 的 run 函數(shù)中,不斷通過(guò) queueSendMap 隊(duì)列讀取信息。
class SendWorker extends ZooKeeperThread {Long sid;Socket sock;volatile boolean running = true;DataInputStream din;final SendWorker sw;public void run() {threadCnt.incrementAndGet();while (running && !shutdown && sock != null) {int length = din.readInt();if (length <= 0 || length > PACKETMAXSIZE) {throw new IOException("Received packet with invalid packet: "+ length);}byte[] msgArray = new byte[length];din.readFully(msgArray, 0, length);ByteBuffer message = ByteBuffer.wrap(msgArray);addToRecvQueue(new Message(message.duplicate(), sid));}}}除了接收信息的功能外,QuorumCnxManager 內(nèi)還定義了一個(gè) SendWorker 內(nèi)部類(lèi)用來(lái)向集群中的其他服務(wù)器發(fā)送投票信息。如下面的代碼所示。在 SendWorker 類(lèi)中,不會(huì)立刻將投票信息發(fā)送到 ZooKeeper 集群中,而是將投票信息首先插入到 pollSendQueue 隊(duì)列,之后通過(guò) send 函數(shù)進(jìn)行發(fā)送。
class SendWorker extends ZooKeeperThread {Long sid;Socket sock;RecvWorker recvWorker;volatile boolean running = true;DataOutputStream dout;public void run() {while (running && !shutdown && sock != null) {ByteBuffer b = null;try {ArrayBlockingQueue<ByteBuffer> bq = queueSendMap.get(sid);if (bq != null) {b = pollSendQueue(bq, 1000, TimeUnit.MILLISECONDS);} else {LOG.error("No queue of incoming messages for " +"server " + sid);break;}if(b != null){lastMessageSent.put(sid, b);send(b);}} catch (InterruptedException e) {LOG.warn("Interrupted while waiting for message on queue",e);}}}}實(shí)現(xiàn)了投票信息的發(fā)送與接收后,接下來(lái)我們就來(lái)看看如何處理投票結(jié)果。
在 ZooKeeper 的底層,是通過(guò) FastLeaderElection 類(lèi)實(shí)現(xiàn)的。如下面的代碼所示,在 FastLeaderElection 的內(nèi)部,定義了最大通信間隔 maxNotificationInterval、服務(wù)器等待時(shí)間 finalizeWait 等屬性配置
public class FastLeaderElection implements Election {final static int maxNotificationInterval = 60000;final static int IGNOREVALUE = -1QuorumCnxManager manager;}在 ZooKeeper 底層通過(guò) getVote 函數(shù)來(lái)設(shè)置本機(jī)的投票內(nèi)容,如下面的代碼所示,在 getVote 中通過(guò) proposedLeader 服務(wù)器信息、proposedZxid 服務(wù)器 ZXID、proposedEpoch 投票輪次等信息封裝投票信息。
synchronized public Vote getVote(){return new Vote(proposedLeader, proposedZxid, proposedEpoch);}在完成投票信息的封裝以及投票信息的接收和發(fā)送后。一個(gè) ZooKeeper 集群中,Leader 服務(wù)器選舉底層實(shí)現(xiàn)的關(guān)鍵步驟就已經(jīng)介紹完了。 Leader 節(jié)點(diǎn)的底層實(shí)現(xiàn)過(guò)程的邏輯相基本分為封裝投票信息、發(fā)送投票、接收投票等。
完了 走了
總結(jié)
以上是生活随笔為你收集整理的Apache ZooKeeper - 选举Leader源码流程深度解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Apache ZooKeeper - L
- 下一篇: leetcode - two-sum