<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          大廠開始放棄ZooKeeper,還用學(xué)嗎?

          共 17412字,需瀏覽 35分鐘

           ·

          2024-11-14 08:45


          ??目錄


          1 ZooKeeper,時(shí)代棄子?

          2 ZooKeeper 核心通識(shí)

          3 典型應(yīng)用場(chǎng)景




          過(guò)去幾年間,國(guó)內(nèi)外大廠紛紛放棄使用 ZooKeeper,與其捆綁甚深的 Kafka 也在 2.8 版本后放棄了它,不禁讓人懷疑——ZooKeeper 要涼了嗎?然而從歷史發(fā)展的潮流看,每一門技術(shù)的消亡并不那么迅猛,甚至有可能隨著社區(qū)團(tuán)隊(duì)的自救在新時(shí)代做出新的成績(jī),比如云原生時(shí)代下的 Java 新特性。基于此,你或許還是應(yīng)該全面地去了解 ZooKeeper 的核心通識(shí)。

          關(guān)注騰訊云開發(fā)者,一手技術(shù)干貨提前解鎖??

          //////////

          11 月 14 日晚 8 點(diǎn),騰訊云開發(fā)者視頻號(hào)揭秘《北大人在鵝廠寫代碼有神器?》,預(yù)約看看大佬在工作中都有哪些“秘密武器”!觀看直播還有機(jī)會(huì)搶鵝廠周邊好禮!







          01



          ZooKeeper,時(shí)代棄子?

          為了應(yīng)對(duì)大流量,現(xiàn)代應(yīng)用/中間件通常采用分布式部署,此時(shí)不得不考慮 CAP 問(wèn)題。ZooKeeper(后文簡(jiǎn)稱 ZK)是面向 CP 設(shè)計(jì)的一個(gè)開源的分布式協(xié)調(diào)框架,將那些復(fù)雜且容易出錯(cuò)的分布式一致性服務(wù)封裝起來(lái),構(gòu)成一個(gè)高效可靠的原語(yǔ)集,并以一系列簡(jiǎn)單易用的接口提供給用戶使用,分布式應(yīng)用程序可以基于它實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布/訂閱、負(fù)載均衡、命名服務(wù)、集群管理、Master 選舉、分布式鎖、分布式隊(duì)列等功能。


          但最近幾年,業(yè)界卻逐漸發(fā)現(xiàn),互聯(lián)網(wǎng)大廠紛紛放棄了對(duì) ZooKeeper 的選型,一時(shí)引起爭(zhēng)議無(wú)數(shù)。不僅如此,與 ZooKeeper 捆綁甚深的 Kafka 也在 2.8 版本里宣布了放棄 ZooKeeper,而在之前的版本中,沒(méi)有 ZooKeeper 甚至無(wú)法運(yùn)行。


          隨著云原生技術(shù)的興起和分布式系統(tǒng)規(guī)模的擴(kuò)大,Zookeeper在處理大規(guī)模集群和快速擴(kuò)展時(shí)的性能瓶頸日益顯現(xiàn)。這是它開始被逐漸放棄、替代的原因之一。


          從技術(shù)的角度來(lái)看,歷史的車輪在不斷向前滾動(dòng),學(xué)術(shù)界和工業(yè)界的理論基礎(chǔ)一直在不斷進(jìn)化,技術(shù)也要適應(yīng)不斷革新的業(yè)務(wù)不停去演進(jìn)。但是合適的組件總會(huì)出現(xiàn)在合適的地方,這就是架構(gòu)師和研發(fā)人員的工作和責(zé)任,也是我們應(yīng)該繼續(xù)去學(xué)習(xí) ZooKeeper 的原因。




          02



          ZooKeeper 核心通識(shí)

             2.1 數(shù)據(jù)結(jié)構(gòu)


          ZK 在內(nèi)存中維護(hù)了一個(gè)類似文件系統(tǒng)的樹狀數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)命名空間(如下),樹中的節(jié)點(diǎn)稱為 znode


          然而,znode 要比文件系統(tǒng)的路徑復(fù)雜,既可以通過(guò)路徑訪問(wèn),又可以存儲(chǔ)數(shù)據(jù)。znode 具有四個(gè)屬性 data、acl、stat、children,如下:

              
          public class DataNode implements Record { byte data[]; Long acl; public StatPersisted stat; private Set<String> children = null; }

          • data:znode 相關(guān)的業(yè)務(wù)數(shù)據(jù)均存儲(chǔ)在這里,但是,父節(jié)點(diǎn)不可存儲(chǔ)數(shù)據(jù);
          • children:存儲(chǔ)當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)引用信息,因?yàn)閮?nèi)存限制,所以 znode 的子節(jié)點(diǎn)數(shù)不是無(wú)限的;
          • stat:包含 znode 節(jié)點(diǎn)的狀態(tài)信息,比如: 事務(wù) id、版本號(hào)、時(shí)間戳等,其中事務(wù) id 和 ZK 的數(shù)據(jù)一直性、選主相關(guān),下面將重點(diǎn)介紹;
          • acl:記錄客戶端對(duì) znode 節(jié)點(diǎn)的訪問(wèn)權(quán)限;

          注意:znode 的數(shù)據(jù)操作具有原子性,讀操作將獲取與節(jié)點(diǎn)相關(guān)的所有數(shù)據(jù),寫操作也將替換掉節(jié)點(diǎn)的所有數(shù)據(jù)。znode 可存儲(chǔ)的最大數(shù)據(jù)量是 1MB ,但實(shí)際上我們?cè)?znode 的數(shù)據(jù)量應(yīng)該盡可能小,因?yàn)閿?shù)據(jù)過(guò)大會(huì)導(dǎo)致zk的性能明顯下降。每個(gè) ZNode 都對(duì)應(yīng)一個(gè)唯一的路徑。

             2.1.1 事物 ID:Zxid

          Zxid 由 Leader 節(jié)點(diǎn)生成。當(dāng)有新寫入事件時(shí),Leader 節(jié)點(diǎn)生成新的 Zxid,并隨提案一起廣播。Zxid 的生成規(guī)則如下:


          • epoch:任期/紀(jì)元,Zxid 的高32位, ZAB 協(xié)議通過(guò) epoch 編號(hào)來(lái)區(qū)分Leader 周期變化,每次一個(gè) leader 被選出來(lái),它都會(huì)有一個(gè)新的 epoch=(原來(lái)的 epoch+1),標(biāo)識(shí)當(dāng)前屬于那個(gè)leader的 統(tǒng)治時(shí)期;可以假設(shè) leader 就像皇帝,epoch 則相當(dāng)于年號(hào),每個(gè)皇帝都有自己的年號(hào);
          • 事務(wù)計(jì)數(shù)器:Zxid 的低32位,每次數(shù)據(jù)變更,計(jì)數(shù)器都會(huì)加一;

          zxid 是遞增的,所以誰(shuí)的 zxid 越大,就表示誰(shuí)的數(shù)據(jù)是最新的。每個(gè)節(jié)點(diǎn)都保存了當(dāng)前最近一次事務(wù)的 Zxid。Zxid 對(duì)于 ZK 的數(shù)據(jù)一致性以及選主都有著重要意義,后邊在介紹相關(guān)知識(shí)時(shí)會(huì)重點(diǎn)講解其作用原理。

             2.1.2 znode 類型


          節(jié)點(diǎn)根據(jù)生命周期的不同可以將劃分為持久節(jié)點(diǎn)臨時(shí)節(jié)點(diǎn)。持久節(jié)點(diǎn)的存活時(shí)間不依賴于客戶端會(huì)話,只有客戶端在顯式執(zhí)行刪除節(jié)點(diǎn)操作時(shí),節(jié)點(diǎn)才消失;臨時(shí)節(jié)點(diǎn)的存活時(shí)間依賴于客戶端會(huì)話,當(dāng)會(huì)話結(jié)束,臨時(shí)節(jié)點(diǎn)將會(huì)被自動(dòng)刪除(當(dāng)然也可以手動(dòng)刪除臨時(shí)節(jié)點(diǎn))。注意:臨時(shí)節(jié)點(diǎn)不能擁有子節(jié)點(diǎn)。

          節(jié)點(diǎn)類型是在創(chuàng)建時(shí)進(jìn)行制定,后續(xù)不能改變。如 create /n1 node1 創(chuàng)建了一個(gè)數(shù)據(jù)為”node1”的持久節(jié)點(diǎn)/n1;在上述指令基礎(chǔ)上加上參數(shù)-e:create -e /n1/n3 node3,則創(chuàng)建了一個(gè)數(shù)據(jù)為”node3”的臨時(shí)節(jié)點(diǎn) /n1/n3。

          create 命令還有一個(gè)可選參數(shù)-s 用于指定創(chuàng)建的節(jié)點(diǎn)是否具有順序特性創(chuàng)建順序節(jié)點(diǎn)時(shí),zk 會(huì)在路徑后面自動(dòng)追加一個(gè) 遞增的序列號(hào),這個(gè)序列號(hào)可以保證在同一個(gè)父節(jié)點(diǎn)下是唯一的,利用該特性我們可以實(shí)現(xiàn)分布式鎖 等功能。

          基于znode的上述兩組特性,兩兩組合后可構(gòu)建4種類型的節(jié)點(diǎn):
          • PERSISTENT:永久節(jié)點(diǎn)
          • EPHEMERAL:臨時(shí)節(jié)點(diǎn)
          • PERSISTENT_SEQUENTIAL:永久順序節(jié)點(diǎn)
          • EPHEMERAL_SEQUENTIAL:臨時(shí)順序節(jié)點(diǎn)

             2.2 Watcher 監(jiān)聽機(jī)制


          Watcher 監(jiān)聽機(jī)制是ZK非常重要的一個(gè)特性。ZK 允許 Client 端在指定節(jié)點(diǎn)上注冊(cè) Watcher,監(jiān)聽節(jié)點(diǎn)數(shù)據(jù)變更、節(jié)點(diǎn)刪除、子節(jié)點(diǎn)狀態(tài)變更等事件,當(dāng)特定事件發(fā)生時(shí),ZK 服務(wù)端會(huì)異步通知注冊(cè)了相應(yīng) Watcher 的客戶端,通過(guò)該機(jī)制,我們可以利用 ZK 實(shí)現(xiàn)數(shù)據(jù)的發(fā)布和訂閱等功能。

          Watcher 監(jiān)聽機(jī)制由三部分協(xié)作完成:ZK 服務(wù)端、ZK 客戶端、客戶端的 WatchManager 對(duì)象。工作時(shí),客戶端首先將 Watcher 注冊(cè)到服務(wù)端,同時(shí)將 Watcher 對(duì)象保存到客戶端的 Watch 管理器中。當(dāng) ZK 服務(wù)端監(jiān)聽的數(shù)據(jù)狀態(tài)發(fā)生變化時(shí),服務(wù)端會(huì)主動(dòng)通知客戶端,接著客戶端的 Watch 管理器會(huì)觸發(fā)相關(guān) Watcher 來(lái)回調(diào)相應(yīng)處理邏輯。


          注意:
          • watcher 變更通知是一次性的:當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候, ZK 會(huì)產(chǎn)生一個(gè) watcher 事件,并且會(huì)發(fā)送到客戶端。但是客戶端只會(huì)收到一次通知。如果后續(xù)這個(gè)節(jié)點(diǎn)再次發(fā)生變化,那么之前設(shè)置 Watcher 的客戶端不會(huì)再次收到消息。可以通過(guò)循環(huán)監(jiān)聽去達(dá)到永久監(jiān)聽效果。
          • 客戶端 watcher 順序回調(diào):watcher 回調(diào)是順序串行化執(zhí)行的,只有回調(diào)后客戶端才能看到節(jié)點(diǎn)最新的狀態(tài)。watcher 回調(diào)邏輯不應(yīng)太復(fù)雜,否則可能影響 watcher 執(zhí)行。
          • 不會(huì)告訴節(jié)點(diǎn)變化前后的具體內(nèi)容:watchEvent 是最小的通信單元,結(jié)構(gòu)上包含通知狀態(tài)、事件類型和節(jié)點(diǎn)路徑,但是,不會(huì)告訴節(jié)點(diǎn)變化前后的具體內(nèi)容。
          • 時(shí)效性:watcher 只有在當(dāng)前 session 徹底失效時(shí)才會(huì)無(wú)效,若在 session 有效期內(nèi)快速重連成功,則 watcher 依然存在,仍可收到事件通知。

             2.3 ZK 集群


          為了確保服務(wù)的高可用性,ZK 采用集群化部署,如下:


          ZK 集群服務(wù)器有三種角色:Leader、Follower 和 Observer
          • Leader:一個(gè) ZK 集群同一時(shí)間只會(huì)有一個(gè)實(shí)際工作的 Leader,它會(huì)發(fā)起并維護(hù)與各 Follwer 及 Observer 間的心跳。所有的寫操作必須要通過(guò) Leader 完成再由 Leader 將寫操作廣播給其它服務(wù)器。
          • Follower:一個(gè) ZK 集群可同時(shí)存在多個(gè) Follower,它會(huì)響應(yīng) Leader 的心跳。Follower 可直接處理并返回客戶端的讀請(qǐng)求,同時(shí)會(huì)將寫請(qǐng)求轉(zhuǎn)發(fā)給 Leader 處理,參與事務(wù)請(qǐng)求 Proposal 的投票及 Leader 選舉投票。
          • Observer:Observer 是3.3.0 版本開始引入的一個(gè)服務(wù)器角色,一個(gè) ZK 集群可同時(shí)存在多個(gè) Observer, 功能與 Follower 類似,但是,不參與投票。

          “早期的 ZooKeeper 集群服務(wù)運(yùn)行過(guò)程中,只有 Leader 服務(wù)器和 Follow 服務(wù)器。隨著集群規(guī)模擴(kuò)大,follower 變多,ZK 在創(chuàng)建節(jié)點(diǎn)和選主等事務(wù)性請(qǐng)求時(shí),需要一半以上節(jié)點(diǎn) AC,所以導(dǎo)致性能下降寫入操作越來(lái)越耗時(shí),follower 之間通信越來(lái)越耗時(shí)。為了解決這個(gè)問(wèn)題,就引入了觀察者,可以處理讀,但是不參與投票。既保證了集群的擴(kuò)展性,又避免過(guò)多服務(wù)器參與投票導(dǎo)致的集群處理請(qǐng)求能力下降。”

          ZK 集群中通常有很多服務(wù)器,那么如何區(qū)分不同的服務(wù)器的角色呢?可以通過(guò)服務(wù)器的狀態(tài)進(jìn)行區(qū)分
          • LOOKING:尋找 Leader 狀態(tài)。當(dāng)服務(wù)器處于該狀態(tài)時(shí),它會(huì)認(rèn)為當(dāng)前集群中沒(méi)有 Leader,因此需要進(jìn)入 Leader 選舉狀態(tài)。
          • LEADING:領(lǐng)導(dǎo)者狀態(tài)。表明當(dāng)前服務(wù)器角色是 Leader。
          • FOLLOWING:跟隨者狀態(tài),同步 leader 狀態(tài),參與投票。表明當(dāng)前服務(wù)器角色是 Follower。
          • OBSERVING:觀察者狀態(tài),同步 leader 狀態(tài),不參與投票。表明當(dāng)前服務(wù)器角色是 Observer。

          ZK 集群是一主多從的結(jié)構(gòu),所有的所有的寫操作必須要通過(guò) Leader 完成,F(xiàn)ollower 可直接處理并返回客戶端的讀請(qǐng)求。那么如何保證從 Follower 服務(wù)器讀取的數(shù)據(jù)與 Leader 寫入的數(shù)據(jù)的一致性呢?Leader 萬(wàn)一由于某些原因崩潰了,如何選出新的 Leader,如何保證數(shù)據(jù)恢復(fù)?Leader 是怎么選出來(lái)的?

             2.3.1 Zab 一致性協(xié)議


          ZK 專門設(shè)計(jì)了 ZAB 協(xié)議(Zookeeper Atomic Broadcast)來(lái)保證主從節(jié)點(diǎn)數(shù)據(jù)的一致性。下面分別從 client 向 Leader 和 Follower 寫數(shù)據(jù)場(chǎng)景展開陳述。

          寫 Leader 場(chǎng)景數(shù)據(jù)一致性


          1. 客戶端向 Leader 發(fā)起寫請(qǐng)求。
          2. Leader 將寫請(qǐng)求以 Proposal 的形式發(fā)給所有 Follower 并等待 ACK。
          3. Follower 收到 Leader 的 Proposal 后返回 ACK。
          4. Leader 得到過(guò)半數(shù)的 ACK(Leader 對(duì)自己默認(rèn)有一個(gè) ACK)后向所有的 Follower 和 Observer 發(fā)送 Commmit。
          5. Leader 將處理結(jié)果返回給客戶端。

          注意:
          • Leader 不需要得到所有 Follower 的 ACK,只要收到過(guò)半的 ACK 即可,同時(shí)Leader 本身對(duì)自己有一個(gè)ACK。上圖中有2個(gè) Follower,只需其中兩個(gè)返回ACK即可,因?yàn)?1+1) / (2+1) > 1/2。
          • Observer 雖然無(wú)投票權(quán),但仍須同步 Leader 的數(shù)據(jù)從而在處理讀請(qǐng)求時(shí)可以返回盡可能新的數(shù)據(jù)。

          寫 Follower 場(chǎng)景數(shù)據(jù)一致性


          1. 客戶端向 Follower 發(fā)起寫請(qǐng)求, Follower 將寫請(qǐng)求轉(zhuǎn)發(fā)給 Leader 處理;
          2. 其它流程與直接寫 Leader 無(wú)任何區(qū)別。

          注意:Observer 與 Follower 寫流程相同。

          最終一致性

          Zab 協(xié)議消息廣播使用兩階段提交的方式,達(dá)到主從數(shù)據(jù)的最終一致性。為什么是最終一致性呢?從上文可知數(shù)據(jù)寫入過(guò)程核心分成下面兩階段:
          1. 第一階段:Leader數(shù)據(jù)寫入事件作為提案廣播給所有 Follower 結(jié)點(diǎn);可以寫入的Follower結(jié)點(diǎn)返回確認(rèn)信息 ACK。
          2. 第二階段:Leader 收到一半以上的 ACK 信息后確認(rèn)寫入可以生效,向所有結(jié)點(diǎn)廣播 COMMIT 將提案生效。

          根據(jù)寫入過(guò)程的兩階段的描述,可以知道 ZooKeeper 保證的是最終一致性,即 Leader 向客戶端返回寫入成功后,可能有部分 Follower 還沒(méi)有寫入最新的數(shù)據(jù),所以是最終一致性。ZooKeeper 保證的最終一致性也叫順序一致性,即每個(gè)結(jié)點(diǎn)的數(shù)據(jù)都是嚴(yán)格按事務(wù)的發(fā)起順序生效的。ZooKeeper 集群的寫入是由 Leader 結(jié)點(diǎn)協(xié)調(diào)的,真實(shí)場(chǎng)景下寫入會(huì)有一定的并發(fā)量,那 Zab 協(xié)議的兩階段提交是如何保證事務(wù)嚴(yán)格按順序生效的呢?ZK 事物的順序性是借助上文中的Zxid實(shí)現(xiàn)的。Leader 在收到半數(shù)以上 ACK 后會(huì)將提案生效并廣播給所有 Follower 結(jié)點(diǎn),Leader 為了保證提案按 ZXID 順序生效,使用了一個(gè) ConcurrentHashMap,記錄所有未提交的提案,命名為 outstandingProposals,key 為 ZXID,Value 為提案的信息。對(duì) outstandingProposals 的訪問(wèn)邏輯如下:
          1. Leader 每發(fā)起一個(gè)提案,會(huì)將提案的 ZXID 和內(nèi)容放到 outstandingProposals 中,作為待提交的提案;
          2. Leader收到 Follower 的 ACK 信息后,根據(jù) ACK 中的 ZXID 從 outstandingProposals 中找到對(duì)應(yīng)的提案,對(duì) ACK 計(jì)數(shù);
          3. 執(zhí)行 tryToCommit 嘗試將提案提交:判斷流程是,先判斷當(dāng)前 ZXID 之前是否還有未提交提案,如果有,當(dāng)前提案暫時(shí)不能提交;再判斷提案是否收到半數(shù)以上 ACK,如果達(dá)到半數(shù)則可以提交;如果可以提交,將當(dāng)前 ZXID 從 outstandingProposals 中清除并向 Followers 廣播提交當(dāng)前提案;

          Leader 是如何判斷當(dāng)前 ZXID 之前是否還有未提交提案的呢?由于前提是保證順序提交的,所以 Leader 只需判斷 outstandingProposals 里,當(dāng)前 ZXID 的前一個(gè) ZXID 是否存在。代碼如下:


          所以 ZooKeeper 是通過(guò)兩階段提交保證數(shù)據(jù)的最終一致性,并且通過(guò)嚴(yán)格按照 ZXID 的順序生效提案保證其順序一致性的。

             2.3.2 選主原理


          ZK中默認(rèn)的并建議使用的 Leader 選舉算法是:基于 TCP 的 FastLeaderElection。在分析選舉原理前,先介紹幾個(gè)重要的參數(shù)。

          • 服務(wù)器 ID(myid):每個(gè) ZooKeeper 服務(wù)器,都需要在數(shù)據(jù)文件夾下創(chuàng)建一個(gè)名為 myid 的文件,該文件包含整個(gè) ZooKeeper 集群唯一的 ID(整數(shù))。該參數(shù)在選舉時(shí)如果無(wú)法通過(guò)其他判斷條件選擇 Leader,那么將該 ID 的大小來(lái)確定優(yōu)先級(jí)。
          • 事務(wù) ID(zxid):?jiǎn)握{(diào)遞增,值越大說(shuō)明數(shù)據(jù)越新,權(quán)重越大。
          • 邏輯時(shí)鐘(epoch-logicalclock):同一輪投票過(guò)程中的邏輯時(shí)鐘值是相同的,每投完一次值會(huì)增加。
          • ZK 的 leader 選舉存在兩類,一個(gè)是服務(wù)器啟動(dòng)時(shí) leader 選舉,另一個(gè)是運(yùn)行過(guò)程中服務(wù)器宕機(jī)時(shí)的 leader 選舉,下面依次展開介紹。

          服務(wù)器啟動(dòng)時(shí)的 leader 選舉

          1、各自推選自己:ZooKeeper 集群剛啟動(dòng)時(shí),所有服務(wù)器的 logicClock 都為 1,zxid 都為 0。各服務(wù)器初始化后,先把第一票投給自己并將它存入自己的票箱,同時(shí)廣播給其他服務(wù)器。此時(shí)各自的票箱中只有自己投給自己的一票,如下圖所示:


          2、更新選票:第一步中各個(gè)服務(wù)器先投票給自己,并把投給自己的結(jié)果廣播給集群中的其他服務(wù)器,這一步其他服務(wù)器接收到廣播后開始更新選票操作,以 Server1 為例流程如下:
            1. Server1 收到 Server2 和 Server3 的廣播選票后,由于 logicClock 和 zxid 都相等,此時(shí)就比較 myid;
            2. Server1 收到的兩張選票中 Server3 的 myid 最大,此時(shí) Server1 判斷應(yīng)該遵從 Server3 的投票決定,將自己的票改投給 Server3。接下來(lái) Server1 先清空自己的票箱(票箱中有第一步中投給自己的選票),然后將自己的新投票(1->3)和接收到的 Server3 的(3->3)投票一起存入自己的票箱,再把自己的新投票決定(1->3)廣播出去,此時(shí) Server1 的票箱中有兩票:(1->3),(3->3);
            3. 同理,Server2 收到 Server3 的選票后也將自己的選票更新為(2->3)并存入票箱然后廣播。此時(shí) Server2 票箱內(nèi)的選票為(2->3),(3->3);
            4. Server3 根據(jù)上述規(guī)則,無(wú)須更新選票,自身的票箱內(nèi)選票仍為(3->3);
            5. Server1 與 Server2 重新投給 Server3 的選票廣播出去后,由于三個(gè)服務(wù)器最新選票都相同,最后三者的票箱內(nèi)都包含三張投給服務(wù)器 3 的選票。


          3、根據(jù)選票確定角色:根據(jù)上述選票,三個(gè)服務(wù)器一致認(rèn)為此時(shí) Server3 應(yīng)該是 Leader。因此 Server1 和 Server2 都進(jìn)入 FOLLOWING 狀態(tài),而 Server3 進(jìn)入 LEADING 狀態(tài)。之后 Leader 發(fā)起并維護(hù)與 Follower 間的心跳。


          運(yùn)行時(shí) Follower 重啟選舉

          本節(jié)討論 Follower 節(jié)點(diǎn)發(fā)生故障重啟或網(wǎng)絡(luò)產(chǎn)生分區(qū)恢復(fù)后如何進(jìn)行選舉。

          1、Follower 重啟投票給自己:Follower 重啟,或者發(fā)生網(wǎng)絡(luò)分區(qū)后找不到 Leader,會(huì)進(jìn)入 LOOKING 狀態(tài)并發(fā)起新的一輪投票。


          2、發(fā)現(xiàn)已有 Leader 后成為 Follower:Server3 收到 Server1 的投票后,將自己的狀態(tài) LEADING 以及選票返回給 Server1。Server2 收到 Server1 的投票后,將自己的狀態(tài) FOLLOWING 及選票返回給 Server1。此時(shí) Server1 知道 Server3 是 Leader,并且通過(guò) Server2 與 Server3 的選票可以確定 Server3 確實(shí)得到了超過(guò)半數(shù)的選票。因此服務(wù)器 1 進(jìn)入 FOLLOWING 狀態(tài)。


          運(yùn)行時(shí) Leader 重啟選舉

          1、Follower 發(fā)起新投票:Leader(Server3)宕機(jī)后,F(xiàn)ollower(Server1 和 2)發(fā)現(xiàn) Leader 不工作了,因此進(jìn)入 LOOKING 狀態(tài)并發(fā)起新的一輪投票,并且都將票投給自己,同時(shí)將投票結(jié)果廣播給對(duì)方。


          2、更新選票:(1)Server1 和 2 根據(jù)外部投票確定是否要更新自身的選票,這里跟之前的選票 PK 流程一樣,比較的優(yōu)先級(jí)為:logicLock > zxid > myid,這里 Server1 的參數(shù)(L=3, M=1, Z=11)和 Server2 的參數(shù)(L=3, M=2, Z=10),logicLock 相等,zxid 服務(wù)器 1 大于服務(wù)器 2,因此服務(wù)器 2 就清空已有票箱,將(1->1)和(2->1)兩票存入票箱,同時(shí)將自己的新投票廣播出去 (2)服務(wù)器 1 收到 2 的投票后,也將自己的票箱更新。


          3、重新選出 Leader:此時(shí)由于只剩兩臺(tái)服務(wù)器,服務(wù)器 1 投票給自己,服務(wù)器 2 投票給 1,所以 1 當(dāng)選為新 Leader。


          4、舊 Leader 恢復(fù)發(fā)起選舉:之前宕機(jī)的舊 Leader 恢復(fù)正常后,進(jìn)入 LOOKING 狀態(tài)并發(fā)起新一輪領(lǐng)導(dǎo)選舉,并將選票投給自己。此時(shí)服務(wù)器 1 會(huì)將自己的 LEADING 狀態(tài)及選票返回給服務(wù)器 3,而服務(wù)器 2 將自己的 FOLLOWING 狀態(tài)及選票返回給服務(wù)器 3。


          5、舊 Leader 成為 Follower:服務(wù)器 3 了解到 Leader 為服務(wù)器 1,且根據(jù)選票了解到服務(wù)器 1 確實(shí)得到過(guò)半服務(wù)器的選票,因此自己進(jìn)入 FOLLOWING 狀態(tài)。


          腦裂

          對(duì)于一主多從類的集群應(yīng)用,通常要考慮腦裂問(wèn)題,腦裂會(huì)導(dǎo)致數(shù)據(jù)不一致。那么,什么是腦裂?簡(jiǎn)單點(diǎn)來(lái)說(shuō),就是一個(gè)集群有兩個(gè) master。通常腦裂產(chǎn)生原因如下:
          1. 假死:由于心跳超時(shí)(網(wǎng)絡(luò)原因?qū)е碌模┱J(rèn)為 Leader 死了,但其實(shí) Leader 還存活著。
          2. 腦裂:由于假死會(huì)發(fā)起新的Leader選舉,選舉出一個(gè)新的 Leader,但舊的 Leader 網(wǎng)絡(luò)又通了,導(dǎo)致出現(xiàn)了兩個(gè) Leader ,有的客戶端連接到老的 Leader,而有的客戶端則連接到新的 Leader。

          通常解決腦裂問(wèn)題有 Quorums(法定人數(shù))方式、Redundant communications(冗余通信)方式、仲裁、磁盤鎖等方式。ZooKeeper 采用 Quorums 這種方式來(lái)防止“腦裂”現(xiàn)象,只有集群中超過(guò)半數(shù)節(jié)點(diǎn)投票才能選舉出 Leader。



          03



          典型應(yīng)用場(chǎng)景

             3.1 數(shù)據(jù)發(fā)布/訂閱

          我們可基于 ZK 的 Watcher 監(jiān)聽機(jī)制實(shí)現(xiàn)數(shù)據(jù)的發(fā)布與訂閱功能。ZK 的發(fā)布訂閱模式采用的是推拉結(jié)合的方式實(shí)現(xiàn)的,實(shí)現(xiàn)原理如下:


          1. 當(dāng)集群中的服務(wù)啟動(dòng)時(shí),客戶端向 ZK 注冊(cè) watcher 監(jiān)聽特定節(jié)點(diǎn),并從節(jié)點(diǎn)拉取數(shù)據(jù)獲取配置信息;
          2. 當(dāng)發(fā)布者變更配置時(shí),節(jié)點(diǎn)數(shù)據(jù)發(fā)生變化,ZK 會(huì)發(fā)送 watcher 事件給各個(gè)客戶端;客戶端在接收到 watcher 事件后,會(huì)從該節(jié)點(diǎn)重新拉取數(shù)據(jù)獲取最新配置信息。

          注意:Watch 具有一次性,所以當(dāng)獲得服務(wù)器通知后要再次添加 Watch 事件。

             3.2 負(fù)載均衡

          利用 ZK 的臨時(shí)節(jié)點(diǎn)、watcher 機(jī)制等特性可實(shí)現(xiàn)負(fù)載均衡,具體思路如下:


          把 ZK 作為一個(gè)服務(wù)的注冊(cè)中心,基本流程:
          1. 服務(wù)提供者 server 啟動(dòng)時(shí)在 ZK 進(jìn)行服務(wù)注冊(cè)(創(chuàng)建臨時(shí)文件);
          2. 服務(wù)消費(fèi)者 client 啟動(dòng)時(shí),請(qǐng)求 ZK 獲取最新的服務(wù)存活列表并注冊(cè) watcher,然后將獲得服務(wù)列表保存到本地緩存中;
          3. client 請(qǐng)求 server 時(shí),根據(jù)自己的負(fù)載均衡算法,從服務(wù)器列表選取一個(gè)進(jìn)行通信;
          4. 若在運(yùn)行過(guò)程中,服務(wù)提供者出現(xiàn)異常或人工關(guān)閉不能提供服務(wù),臨時(shí)節(jié)點(diǎn)失效,ZK 探測(cè)到變化更新本地服務(wù)列表并異步通知到服務(wù)消費(fèi)者,服務(wù)消費(fèi)者監(jiān)聽到服務(wù)列表的變化,更新本地緩存。

          注意:服務(wù)發(fā)現(xiàn)可能存在延遲,因?yàn)榉?wù)提供者掛掉到緩存更新大約需要3-5s的時(shí)間(根據(jù)網(wǎng)絡(luò)環(huán)境不同還需仔細(xì)測(cè)試)。為了保證服務(wù)的實(shí)時(shí)可用,client 請(qǐng)求 server 發(fā)生異常時(shí),需要根據(jù)服務(wù)消費(fèi)報(bào)錯(cuò)信息,進(jìn)行重負(fù)載均衡重試等。

             3.3 命名服務(wù)

          命名服務(wù)是指通過(guò)指定的名字來(lái)獲取資源或者服務(wù)的地址、提供者等信息。以 znode 的路徑為名字,znode 存儲(chǔ)的數(shù)據(jù)為值,可以很容易構(gòu)建出一個(gè)命名服務(wù)。例如 Dubbo 使用 ZK 來(lái)作為其命名服務(wù),如下:


          • 所有 Dubbo 相關(guān)的數(shù)據(jù)都組織在 /dubbo 的根節(jié)點(diǎn)下;
          • 二級(jí)目錄是服務(wù)名,如 com.foo.BarService
          • 三級(jí)目錄有兩個(gè)子節(jié)點(diǎn),分別是 providersconsumers ,表示該服務(wù)的提供者和消費(fèi)者;
          • 四級(jí)目錄記錄了與該服務(wù)相關(guān)的每一個(gè)應(yīng)用實(shí)例的 URL 信息,在 providers 下的表示該服務(wù)的所有提供者,而在 consumers 下的表示該服務(wù)的所有消費(fèi)者。舉例說(shuō)明, com.foo.BarService 的服務(wù)提供者在啟動(dòng)時(shí)將自己的 URL 信息注冊(cè)到 /dubbo/com.foo.BarService/providers 下;同樣的,服務(wù)消費(fèi)者將自己的信息注冊(cè)到相應(yīng)的 consumers 下,同時(shí),服務(wù)消費(fèi)者會(huì)訂閱其所對(duì)應(yīng)的 providers 節(jié)點(diǎn),以便能夠感知到服務(wù)提供方地址列表的變化。

             3.4 集群管理

          基于 ZK 的臨時(shí)節(jié)點(diǎn)和 watcher 監(jiān)聽機(jī)制可實(shí)現(xiàn)集群管理。集群管理通常指監(jiān)控集群中各個(gè)主機(jī)的運(yùn)行時(shí)狀態(tài)、存活狀況等信息。如下圖所示,主機(jī)向 ZK 注冊(cè)臨時(shí)節(jié)點(diǎn),監(jiān)控系統(tǒng)注冊(cè)監(jiān)聽集群下的臨時(shí)節(jié)點(diǎn),從而獲取集群中服務(wù)的狀態(tài)等信息。


             3.5 Master 選舉

          ZK 中某節(jié)點(diǎn)同一層子節(jié)點(diǎn),名稱具有唯一性,所以,多個(gè)客戶端創(chuàng)建同一節(jié)點(diǎn)時(shí),只會(huì)有一個(gè)客戶端成功。利用該特性,可以實(shí)現(xiàn) maser 選舉,具體如下:


          多個(gè)客戶端同時(shí)競(jìng)爭(zhēng)創(chuàng)建同一臨時(shí)節(jié)點(diǎn)/master-election/master,最終只能有一個(gè)客戶端成功。這個(gè)成功的客戶端成為 Master,其它客戶端置為 Slave。

          Slave 客戶端都向這個(gè)臨時(shí)節(jié)點(diǎn)的父節(jié)點(diǎn)/master-election 注冊(cè)一個(gè)子節(jié)點(diǎn)列表的 watcher 監(jiān)聽。

          一旦原 Master 宕機(jī),臨時(shí)節(jié)點(diǎn)就會(huì)消失,zk 服務(wù)器就會(huì)向所有 Slave 發(fā)送子節(jié)點(diǎn)變更事件,Slave 在接收到事件后會(huì)競(jìng)爭(zhēng)創(chuàng)建新的 master 臨時(shí)子節(jié)點(diǎn)。誰(shuí)創(chuàng)建成功,誰(shuí)就是新的 Master。

             3.6 分布式鎖

          基于ZK的臨時(shí)順序節(jié)點(diǎn)和 Watcher 機(jī)制可實(shí)現(xiàn)公平分布式鎖。下面具體看下多客戶端獲取及釋放 zk 分布式鎖的整個(gè)流程及背后的原理。

          假如說(shuō)客戶端 A 先發(fā)起請(qǐng)求,就會(huì)搞出來(lái)一個(gè)順序節(jié)點(diǎn),大家看下面的圖,Curator 框架大概會(huì)弄成如下的樣子:


          這一大坨長(zhǎng)長(zhǎng)的名字都是 Curator 框架自己生成出來(lái)的。然后,因?yàn)榭蛻舳?A 是第一個(gè)發(fā)起請(qǐng)求的,所以給他搞出來(lái)的順序節(jié)點(diǎn)的序號(hào)是"1"。接著客戶端 A 會(huì)查一下"my_lock"這個(gè)鎖節(jié)點(diǎn)下的所有子節(jié)點(diǎn),并且這些子節(jié)點(diǎn)是按照序號(hào)排序的,這個(gè)時(shí)候大概會(huì)拿到這么一個(gè)集合:


          接著客戶端 A 會(huì)走一個(gè)關(guān)鍵性的判斷:唉!兄弟,這個(gè)集合里,我創(chuàng)建的那個(gè)順序節(jié)點(diǎn),是不是排在第一個(gè)啊?如果是的話,那我就可以加鎖了啊!因?yàn)槊髅魑揖褪堑谝粋€(gè)來(lái)創(chuàng)建順序節(jié)點(diǎn)的人,所以我就是第一個(gè)嘗試加分布式鎖的人啊!bingo!加鎖成功!大家看下面的圖,再來(lái)直觀的感受一下整個(gè)過(guò)程。


          假如說(shuō)客戶端 A 加完鎖完后,客戶端B過(guò)來(lái)想要加鎖,這個(gè)時(shí)候它會(huì)干一樣的事兒:先是在"my_lock"這個(gè)鎖節(jié)點(diǎn)下創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn),因?yàn)槭堑诙€(gè)來(lái)創(chuàng)建順序節(jié)點(diǎn)的,所以 zk 內(nèi)部會(huì)維護(hù)序號(hào)為"2"。接著客戶端 B 會(huì)走加鎖判斷邏輯,查詢"my_lock"鎖節(jié)點(diǎn)下的所有子節(jié)點(diǎn),按序號(hào)順序排列,此時(shí)看到的類似于:


          同時(shí)檢查自己創(chuàng)建的順序節(jié)點(diǎn),是不是集合中的第一個(gè)?明顯不是,此時(shí)第一個(gè)是客戶端 A 創(chuàng)建的那個(gè)順序節(jié)點(diǎn),序號(hào)為"01"的那個(gè)。所以加鎖失敗!加鎖失敗了以后,客戶端 B 就會(huì)通過(guò) ZK 的 API 對(duì)他的順序節(jié)點(diǎn)的上一個(gè)順序節(jié)點(diǎn)加一個(gè)監(jiān)聽器, 即對(duì)客戶端 A 創(chuàng)建的那個(gè)順序節(jié)加監(jiān)聽器!如下:


          接著,客戶端 A 加鎖之后,可能處理了一些代碼邏輯,然后就會(huì)釋放鎖。那么,釋放鎖是個(gè)什么過(guò)程呢?

          其實(shí)很簡(jiǎn)單,就是把自己在 zk 里創(chuàng)建的那個(gè)順序節(jié)點(diǎn),也就是:


          這個(gè)節(jié)點(diǎn)被刪除。

          刪除了那個(gè)節(jié)點(diǎn)之后,zk 會(huì)負(fù)責(zé)通知監(jiān)聽這個(gè)節(jié)點(diǎn)的監(jiān)聽器,也就是客戶端 B 之前加的那個(gè)監(jiān)聽器,說(shuō):兄弟,你監(jiān)聽的那個(gè)節(jié)點(diǎn)被刪除了,有人釋放了鎖。


          此時(shí)客戶端 B 的監(jiān)聽器感知到了上一個(gè)順序節(jié)點(diǎn)被刪除,也就是排在他之前的某個(gè)客戶端釋放了鎖。

          此時(shí),就會(huì)通知客戶端 B 重新嘗試去獲取鎖,也就是獲取"my_lock"節(jié)點(diǎn)下的子節(jié)點(diǎn)集合,此時(shí)為:


          集合里此時(shí)只有客戶端 B 創(chuàng)建的唯一的一個(gè)順序節(jié)點(diǎn)了!

          然后呢,客戶端 B 判斷自己居然是集合中的第一個(gè)順序節(jié)點(diǎn),bingo!可以加鎖了!直接完成加鎖,運(yùn)行后續(xù)的業(yè)務(wù)代碼即可,運(yùn)行完了之后再次釋放鎖。


          注意:利用 ZK 實(shí)現(xiàn)分布式鎖時(shí)要避免出現(xiàn)驚群效應(yīng)。上述策略中,客戶端 B 通過(guò)監(jiān)聽比其節(jié)點(diǎn)順序小的那個(gè)臨時(shí)節(jié)點(diǎn),解決了驚群效應(yīng)問(wèn)題。

             3.7 分布式隊(duì)列

          基于 ZK 的臨時(shí)順序節(jié)點(diǎn)和 Watcher 機(jī)制可實(shí)現(xiàn)簡(jiǎn)單的 FIFO 分布式隊(duì)列。ZK 分布式隊(duì)列和上節(jié)中的分布式鎖本質(zhì)是一樣的,都是基于對(duì)上一個(gè)順序節(jié)點(diǎn)進(jìn)行監(jiān)聽實(shí)現(xiàn)的。具體原理如下:


          利用順序節(jié)點(diǎn)的有序性,為每個(gè)數(shù)據(jù)在 /FIFO 下創(chuàng)建一個(gè)相應(yīng)的臨時(shí)子節(jié)點(diǎn);且每個(gè)消費(fèi)者均在 /FIFO 注冊(cè)一個(gè) watcher;

          消費(fèi)者從分布式隊(duì)列獲取數(shù)據(jù)時(shí),首先嘗試獲取分布式鎖,獲取鎖后從 /FIFO 獲取序號(hào)最小的數(shù)據(jù),消費(fèi)成功后,刪除相應(yīng)節(jié)點(diǎn);

          由于消費(fèi)者均監(jiān)聽了父節(jié)點(diǎn) /FIFO,所以均會(huì)收到數(shù)據(jù)變化的異步通知,然后重復(fù)2的過(guò)程,嘗試消費(fèi)隊(duì)列數(shù)據(jù)。依此循環(huán),直到消費(fèi)完畢。

          -End-
          原創(chuàng)作者|孫少卡

           


          ZooKeeper 有什么優(yōu)缺點(diǎn)?歡迎評(píng)論留言。我們將選取1則優(yōu)質(zhì)的評(píng)論,送出騰訊Q哥公仔1個(gè)(見(jiàn)下圖)。11月21日中午12點(diǎn)開獎(jiǎng)。


          ????歡迎加入騰訊云開發(fā)者社群,享前沿資訊、大咖干貨,找興趣搭子,交同城好友,更有鵝廠招聘機(jī)會(huì)、限量周邊好禮等你來(lái)~


          (長(zhǎng)按圖片立即掃碼)




          瀏覽 576
          4點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          4點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色精品视频在线观看 | 亚洲精品久久久久久久久久久久久 | 美女扒开嫩嫩的尿囗让人桶出白浆 | 黄片在线免费观看 | 97超碰在 |