1. 程式人生 > >不用程式碼趣講 ZooKeeper 叢集

不用程式碼趣講 ZooKeeper 叢集

![](https://img2020.cnblogs.com/blog/759200/202101/759200-20210124161622816-1605238160.png)

本文作者:HelloGitHub-老荀

Hi,這裡是 HelloGitHub 推出的 HelloZooKeeper 系列,**免費開源、有趣、入門級的 ZooKeeper 教程**,面向有程式設計基礎的新手。 > 專案地址:https://github.com/HelloGitHub-Team/HelloZooKeeper 今天開始我們將深入 ZK 叢集相關知識~ ## 一、為什麼需要叢集 ### 1.1 馬果果病了 ZKr~老規矩~ **馬果果**畢竟年紀大了,這辦事處的事情越來越多,終於有一天扛不住,生病了,住院了,聽醫生說要休息好幾天。辦事處負責人不在的話就不能給村民們提供服務了。大家平時也要注意身體啊~ ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210820699-28728408.png) 一連好幾天都沒收到通知的**坤坤**急死了,還有其他非常依賴辦事處的村民們都一起跑去村委會投訴了,村委會也很無奈啊,最終商量了下,決定請村裡威望同樣很高的著名企業家,太極愛好者並且還是村裡首富的**馬小云**成立第二辦事處,地點離原來的辦事處也很近,同樣負責處理之前**馬果果**辦事處的事務。**馬小云**之前也是辦事處的常客,對其中的流程已經是非常清楚了,一直想為人民做實事的他,欣然答應了下來。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210826954-1619408229.png) 而且已經有了之前的成功經驗,所以直接照搬之前的處理流程就行了。但是細心的村民很快就發現了問題,之前在**馬果果**的辦事處很多已經記錄過的事務,現在都消失了,在新的辦事處這裡需要重新登記,非常不方便,但是**馬小云**表示也沒辦法,**馬果果**病得太突然了,還沒交接過,只能表示讓大家忍忍。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210833956-311184390.png) 就這樣,過了兩週,**馬果果**痊癒出院了,在住院期間他也已經得知了**馬小云**這兩週代他幫助村民解決大小事務,他內心非常感激**馬小云**所做的一切,熱愛工作的他,第一時間就回到了工作崗位,把辦事處的大門重新開啟,也廣播告訴了村民,自己這裡又能辦理事務了,希望大家可以繼續過來。由於**馬小云**畢竟業務能力稍差點,處理速度沒那麼快,導致第二辦事處排隊更嚴重了。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210838482-1839965328.png) 聽到第一辦事處又開張的村民們非常高興,畢竟誰也不希望排長隊浪費時間,於是都來到了第一辦事處 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210842258-431714103.png) 但是呢,**馬果果**休息了兩週,這兩週期間村民的登記的事務,他全部都沒有,村民們紛紛表示這不行啊:“我們不管你們有幾個辦事處,你們得保證資料都是一致的啊!”。村委會也同意村民的訴求,勒令兩個辦事處整改,需要解決這個問題!而且因為**馬果果**資歷更老,更有經驗,所以讓**馬小云**一切聽**馬果果**的指揮,方案也由**馬果果**去想辦法出臺。 ### 1.2 馬果果的新規定 **馬果果**不愧薑還是老的辣,很快就想出了一個好辦法,出臺了一系列的規則: - 資料必須以**馬果果**為主 - 兩個辦事處間需要打通聯絡,隨時保持溝通 - 之前把村民前來登記的事務區分成讀和寫,是非常正確的決定。之後寫操作必須通過**馬果果**,讀操作**馬小云**可以自行解決 但是光出臺規則還不夠,還需要一系列可以落地的操作,於是**馬果果**向村委會申請,辦事處需要擴招人,村委會決定讓**馬果果**放手幹,同意了他的申請。 **馬果果**把自己辦公室的佈置重新調整了下變成了這樣: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210848613-384571333.png) 簡單介紹下新來的同事們: - **小PS**負責區分村民的請求是否需要發起提案,並把請求再次轉發給**小C**以及**小S** - **小C**負責管理**小PS**提案的提交工作,這個職位非常重要,所以**馬果果**很有私心的請了一個妹子來承擔這個職位 - 現在的**小S**不再和**小F**打交道了而是和**小A**打交道,等他歸檔完後就會通知**小A** - 因為現在有兩個辦事處了,所以需要聘請一個話務員,專門負責和隔壁的**馬小云**辦事處進行溝通 光是安排好自己還不夠,**馬果果**替**馬小云**也設計了一套新的辦公室方案: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210853169-1487423323.png) 和**馬果果**不太一樣,這裡也簡單介紹下: - 使用**小FR**替換了原來的**小P**作為辦事處第一接待人 - **小S**也不和**小F**打交道了,直接和**小SA**打交道,等他歸檔完就會通知**小SA** - 和**馬果果**一樣也聘請了一個話務員負責和**馬果果**進行聯絡 --- 原來只有**馬果果**負責的一個辦事處,隨著**馬果果**的病倒,村民的業務就無法繼續展開了,這就是單點故障,所以在原來的基礎上增加一個辦事處,可以增加整個辦事處的吞吐量的同時也可以在一個辦事處無法提供服務時,不至於導致村民們無法使用,這就是高可用。這也是為什麼需要叢集部署的最重要原因! ## 二、第一辦事處 引入了叢集前,原本一個節點資料自己內部運作管理就行,非常方便,但是引入集群后,叢集間的節點如何溝通成了問題,讓我們一起來看看**馬果果**的新員工們是怎麼做的吧? 不同於之前的單機流程,現在流程複雜了很多,增加了很多出場的人物,為了讓大家能快速記憶,我這裡提前把名字的由來劇透給大家: - **小P**對應程式碼中的 `PrepRequestProcessor` 負責預處理 - **小PS**對應程式碼中的 `ProposalRequestProcessor` 負責寫事務的提案 - **小C**對應程式碼中的 `CommitProcessor` 負責對事務請求提交 - **小S**對應程式碼中的 `SyncRequestProcessor` 負責資料的歸檔 - **小A**對應程式碼中的 `AckRequestProcessor` 負責告訴**馬果果**當前事務的 ACK 資訊 - **小F**對應程式碼中的 `FinalRequestProcessor` 負責對記憶體模型的操作 ### 2.1 負責的小PS 原先**小P**在第一時間詢問村民後,並對當次請求進行標記後,就會把該請求轉發給**小PS**,**小PS**做的事情也很簡單: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210858997-1742630138.png) 主要就是看是不是寫請求,如果是的話就要發起提案並且本地要通知**小S**歸檔。 ### 2.2 忙碌的小C **小C**是除了**小F**最忙碌的人了,她在接受到上一個同事傳遞過來的請求後會: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210910454-422796922.png) 不是說**小C**是最忙的嗎?就這? ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210916420-1459717477.png) 別急,**小C**的處理過程的確是比較繁瑣,但是我這裡先給出簡單的流程,最重要的提交操作,我暫時不展開,之後會講~ ### 2.3 小S和小A **小S**處理的流程發生了改變,他前面的同事不再是**小P**,而他處理完歸檔後也不再把請求交給**小F**而是交給**小A**,而**小A**做的事情更簡單,僅僅只是告訴**馬果果**辦事處此次事務請求歸檔成功,其實就是 ACK。 ### 2.4 話務員 為了更順暢的和隔壁的**馬小云**辦事處相互溝通,**馬果果**定下了幾個暗號,而話務員則負責用暗號去通知**馬小云** - REQUEST - PROPOSAL - ACK - COMMIT 當然暗號不止這些,之後有遇到再說。 在具體展開流程細節前,我覺得還是要把**馬小云**的流程簡單介紹下,等兩邊都介紹完後,再合併在一起講解~ ## 三、第二辦事處 同樣因為現在有兩個辦事處的關係,**馬小云**也無法單純使用之前的流程,並且新員工中有明顯區別於**馬果果**的**小FR**和**小SA**,這裡也介紹下: - **小FR**對應程式碼中的 `FollowerRequestProcessor` 負責**馬小云**這邊的預處理 - **小SA**對應程式碼中的 `SendAckRequestProcessor` 和**馬果果**的**小A**類似,通過話務員通知**馬果果**當前事務的 ACK ### 3.1 同樣細心的小FR和小SA ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210925079-1961733564.png) 和**小PS**有點類似,也是需要區分讀寫,但區別是寫請求需要通知**馬果果**。 **小SA**的邏輯是接受到**小S**的歸檔資訊後,把 ACK 通知給**馬果果**,太簡單了就不畫圖了。 ## 四、實戰 剛剛我們把兩個辦事處邏輯都大致介紹了下,但是太過於碎片化了和簡單,所以下面開始進入實戰環節,會分別假定不同的業務場景和複雜程度,從簡單到複雜,把從村民來辦事處登記事務到辦事處處理完成之間的邏輯按照時間順序進行整理。 *前排提醒,多圖預警* ### 4.1 一個讀請求(馬果果) 假設我們的**坤坤**來到**馬果果**的辦事處,想要查詢**雞太美**最新的跳舞視訊 `/雞太美/跳舞` ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210930858-1327155718.png) **小P**首先知道**坤坤**是合法的村民,然後詢問得知,此次來辦事處的目的是為了查詢,就會把此次登記標記為讀請求,就把**坤坤**的請求交給下一個櫃檯的**小PS**。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210936270-2091915978.png) **小PS**拿到請求後,先把請求原封不動的給到了**小C**,之後通過**小P**的標記知道了這是一個讀請求,便不做其他處理。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210945743-140694102.png) **小C**取到這個請求後也發現這是一個讀請求,所以也直接交給了**小F**,自己不需要其他處理。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210954190-1513371484.png) **小F**拿出了小紅本檢視,假設 `/雞太美/跳舞` 存在,把對應的資料就返回給了**坤坤**。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303210957715-834992928.png) **坤坤**拿到了結果心滿意足的回去了並且定好了 17 點的鬧鐘守在電腦前等著**雞太美**的開播了 --- 可以看到一個讀請求的處理流程是非常簡單的,別急,難度會一點點的增加哦 ### 4.2 一個讀請求(馬小云) 同樣還是我們的**坤坤**,但是這次來到**馬小云**的辦事處,同樣想要查詢**雞太美**最新的跳舞視訊 `/雞太美/跳舞` ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211002619-827804756.png) 與**馬果果**不同的是,先處理**坤坤**請求的是**小FR**,他會先把請求發給**小C**,之後通過詢問**坤坤**得知此次目的是查詢,就不會做其他處理。你可能會問,**小FR**不需要對**坤坤**的身份進行核實嗎?我認為可能是因為當前是讀請求所以不會對資料造成破壞,所以並沒有做校驗。 之後的**小C**和**小F**和**馬果果**版本沒有任何不同,就不贅述了。讓我們進入下一個難度吧。 ### 4.3 一個寫請求(馬果果) 寫請求就和讀請求不一樣了,因為根據**馬果果**的規定,兩個辦事處的資料得保持一致,所以就會涉及到如何通知對方了,讓我們一起來看看吧。 假設我們的**坤坤**來到**馬果果**的辦事處,想要為自己建立一個事務登記 `/坤坤/日記` ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211013916-1631571759.png) ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211018243-214095394.png) **小P**知道**坤坤**是合法的村民並且**坤坤**此次的目的是寫資料,所以就給**坤坤**的請求打了一個寫事務的標記,就把請求交給了**小PS**了。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211023934-761371562.png) **小PS**還是先把請求交給了**小C**先處理。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211028585-860843441.png) **小C**看到此次是寫請求就拿出自己的小本子記了下來 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211034924-634116764.png) **小PS**已經得知此次是寫請求。注意!這裡開始就不一樣了,**小PS**會讓話務員給**馬小云**辦事處打電話。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211039858-532310788.png) 話務員告訴他們這次的請求並帶著 PROPOSAL 的暗號。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211043626-516215365.png) 這裡必須要提一下事務編號,為了嚴格保證村民來登記的順序,**馬果果**還規定了必須給每一次的寫事務分配一個唯一的遞增數字,從 0 開始。 並且通知**馬小云**的同時,**馬果果**也會把當前的提案記錄下來: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211049349-640440758.png) --- 這時候我們把視角切換到**馬小云**這邊,**馬小云**的話務員接受到**馬果果**那邊的 PROPOSAL 的暗號後,會直接讓自己這邊的**小S**進行歸檔,**馬小云**則會在備忘錄裡記錄: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211054485-389941712.png) 等**小S**歸檔完後,就會把**坤坤**的請求交給**小SA** ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211058936-170796172.png) **小SA**事情很簡單就是讓話務員通知**馬果果**歸檔完成 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211102850-1280366962.png) 接著**馬小云**這邊的話務員就會給**馬果果**辦事處打電話通知他們歸檔完成 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211106602-394407866.png) --- 視角再一次回到**馬果果**這邊,在**小PS**讓話務員通知**馬小云**那邊的同時**小S**也沒閒著,進行了歸檔的操作 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211110889-778013738.png) **小S**歸檔完成後,會把請求交給**小A**,**小A**做的事情很簡單就是通知**馬果果**此次歸檔完成。 我們這裡假設先是**馬果果**這邊的**小S**歸檔完成,**馬果果**在收到歸檔完成訊息後會拿出剛剛的小本本找到對應的提案記錄,並把已經歸檔完成的給記下來: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211115131-820585141.png) 因為**馬果果**知道一共有兩個辦事處,所以還需要等待**馬小云**的歸檔完成通知。 過了一會會,**馬小云**的歸檔通知也來了,就再在小本本上記下來 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211119477-1790885378.png) 至此,兩個辦事處對於當前提案都已經完成了歸檔,**馬果果**就會讓話務員通知**馬小云**可以提交了,並且會將小本本上事務 0 的這條記錄刪除(圖就不畫了)。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211123351-62988298.png) 通知完,**馬果果**就讓**小C**可以進行提交了 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211128085-261392683.png) **小C**就會拿出剛剛的備忘錄,找到**坤坤**的等待處理的事務的第一條(當前場景只有一條)就是:建立 `/坤坤/日記`。就馬上把這個事務交給了**小F**處理 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211132841-343779618.png) **小F**就會在小紅本上把當前事務記錄下來: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211137845-1138874971.png) 交給**小F**後,**小C**發現**坤坤的**所有事務都處理完了,就把他從備忘錄上刪除了: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211142521-487316387.png) --- 讓我們把視角再切到**馬小云**,**馬果果**這邊的**小C**在處理的時候,**馬小云**的話務員收到了來自**馬果果**的 COMMIT 訊息並告訴了**馬小云**,而**馬小云**會從備忘錄中找出最早的一條請求就是:坤坤,建立,`/坤坤/日記`,然後就會把該請求交給**小C** ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211151142-1284286377.png) 至於之後**小C**處理以及處理完交給**小F**處理,和**馬果果**那邊的邏輯是一樣的,就不贅述了。 至此,一個寫請求(**馬果果**)的基本流程就算完成了。 ### 4.4 一個寫請求(馬小云) 假設我們的**坤坤**這次來到**馬小云**的辦事處,同樣為自己建立一個事務登記 `/坤坤/日記` ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211157757-1155300195.png) **小FR**先把請求交給了**小C**,然後發現了**坤坤**這次來辦理的是寫請求,就會要求話務員通知**馬果果**。 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211201446-929667554.png) 話務員就會打電話給**馬果果**辦事處,並且告訴他們此次的請求以及攜帶上 REQUEST 暗號 而**馬小云**這邊的**小C**會和之前的例子一樣,也會在備忘錄裡記錄下 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211219151-1412651277.png) --- 現在我們把視角切到**馬果果**這邊,話務員接受到 REQUEST 的請求後,告訴了**馬果果**,而**馬果果**會直接把這個請求交給**小P**去處理 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211208546-1131418404.png) 彷彿就是**坤坤**直接來自己辦事處辦理業務一樣,從**小P**之後的流程和之前的例子可以說是一模一樣了,就不贅述了。 我現在舉了 4 種無併發的場景,除了寫請求都很簡單,我這裡就再把寫請求**馬果果**重新用圖畫一遍 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211226448-900924685.png) 為了簡約圖中省略了故事中的話務員以及**馬果果**和**馬小云**,相同顏色代表處於同一時間處理,時間順序從小到大。 好了,讓我們繼續提升難度進入併發實戰,作為一個公共的辦事處是不可能同時只處理一件事務的! ### 4.5 多個村民多種請求 這一章節我不會再從頭開始畫圖了,只畫重要的部分,我們先看看,如果有多個村民的話,那幾個小本本是怎麼記的吧! ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211231914-690144348.png) **小C**的備忘錄的特點總結: - 以村民作為 key,之後的請求是按照請求的順序擺放的佇列 - 佇列中的第一個請求肯定是寫請求,如果是讀請求的話,就根本不會記錄 - 當村民對應的請求佇列為空後,整條記錄刪除 我們以圖中的**坤坤**舉例,假設現在等待的請求是這樣的(我省略了路徑,這裡只關心事務的型別) ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211237101-553852003.png) 當第一個建立的請求入隊後,之後的查詢請求也無法被執行,都需要等到建立請求執行完畢後才能繼續,所以當第一個建立的請求被提交後,之後的查詢1、查詢2、查詢3會被立馬按順序移除出佇列並執行,而查詢4則需要等待前面的刪除和建立全部提交後才會被執行。 這樣的邏輯保證了,同一個客戶端的請求是按照時間順序執行的,不會出現後到的讀請求先於前面的寫請求執行,造成髒讀,但是需要注意的是不同的客戶端的順序是無法保證的,很可能**坤坤**的建立請求還未提交,之後**東東**的查詢操作就能被返回了。 --- **馬果果**的小本本如果有多條記錄的話就是這樣 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211242398-1965340534.png) 你可能會問:**雞太美**的那條記錄不是歸檔完成了嗎,為什麼還在小本本里?因為 ZK 必須保證事務執行的順序!所以只要有比當前事務編號小的其他事務仍然未提交,本事務就不能提交,圖裡就是**雞太美**必須等到**坤坤**和**東東**都提交完才能進行提交! --- **馬小云**這邊也有一個備忘錄,如果有多條記錄的話會是這樣: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211314528-897391700.png) 這個備忘錄其實是一個先進先出的佇列,每次**馬小云**的提交會從佇列中移除最前面的一條記錄來操作。 --- 故事差不多講完了,有些細節用程式設計師的語言再說一下,我其實省略了兩個處理器 : - `ToBeAppliedRequestProcessor`這個處理器在**馬果果**這邊才是緊接著**小C**的,但是我個人感覺下來沒什麼用就不講了,大家有興趣可以去了解下 - `LeaderRequestProcessor` 這個處理器才是**馬果果**的第一個處理器,他的邏輯涉及到會話、ACL,其他就沒什麼用,留到之後有機會講 大家先看下這個圖: ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211319442-1593660064.png) 用紅框標記的都是執行緒物件,主要邏輯都在 `run` 方法中,**小P**和**小S**我們之前就講過了,這裡就多了**小C**和**小FR**。這裡我得提一下,但凡你們只要在 ZK 中看到執行緒物件,那麼他基本上是使用了生產者-消費者的模型,物件內部維護了一個阻塞佇列,我這次就不畫圖了,因為重要的邏輯之前都已經講了。 畫了這麼多圖,這裡先進行下小結: - **馬果果**就是我們平時在 ZK 中聽到的 Leader 節點,負責對寫事務請求發起提案並最終決定提交 - **馬小云**對應就是 ZK 中的 Follower,只能獨自處理讀請求,寫請求需要轉發給 Leader 去處理 - 讀請求無論客戶端請求給哪個服務端的節點,處理流程都相對簡單,可能幾乎不需要節點之間的通訊,自己就能處理(前提是沒有待處理的寫請求) - 寫請求如果客戶端請求到的是 Leader, Leader 就會對此次事務請求在叢集中發起提案,接受到提案的 Follower 各自進行歸檔,並返回給 Leader 成功的 ACK 資訊,Leader 對 ACK 進行統計,達到叢集數量半數以上就叢集中發起 COMMIT 請求,Follower 們接收到提交請求後,才會修改各自記憶體中的資料 - 寫請求如果客戶端請求到的是 Follower,Follower 在本地做簡單記錄後就會把請求轉發給 Leader 去處理,之後和上一條是一樣的情況 - 分散式事務的理論中是有回滾階段的,當叢集中的節點本地提交失敗後,會通知 Leader 失敗資訊,而 Leader 統計 ACK 之後發現本次事務無法提交就會發送回滾的請求給各個節點。但是!很遺憾我並沒有在 ZK 的原始碼中找到和事務回滾有關的邏輯(當然也很有可能是我疏忽了,如果你知道邏輯在哪兒的話,請一定告訴我),我現在的認為**小S**處理歸檔的時候是不允許失敗的,如果失敗報錯了,整個服務節點會報錯退出 ## 五、劇透 在本篇文章的最後,我們再看看動物村又發生了哪些故事吧。 **馬果果**畢竟年事已高,需要定期去醫院進行體檢,就會耽誤辦事處這邊的工作,而漸漸的隨著時間的推移,**馬小云**的業務能力越來越強了,心裡就產生了:憑什麼我要聽這老頭的?於是主動向村委會提議,建議 Leader 的人選要進行選舉,不能就一直讓**馬果果**佔著這個位子,村委會聽後也覺得很有道理,於是拉來**馬果果**一起商量,**馬果果**肯定不能同意啊,但是無奈**馬小云**畢竟是首富,很快就通過上下打點讓村委會一致通過了這個建議 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211330189-658709868.png) 但是現在只有兩個辦事處,如果雙方各執一詞,就平票了,所以村委會再次經過商量決定引入第三個辦事處,這個新辦事處的負責人選擇了村裡的著名企業家**馬小騰**,這樣就不會出現平票的情況,具體選舉規則如下: - 每天三個辦事處開張前必須要先投票選出一個 Leader - Leader 必須是經手處理過事務編號最大的 - 需要各個辦事處半數以上的同意 - 一切以 Leader 為主,讀寫請求遵守之前**馬果果**的定下的流程 - 當 Leader 無法處理事務的時候,需要立即選出新的 Leader,選出之前辦事處不能對外提供服務 所以現在有了三個辦事處成了這樣 ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211337393-454086575.png) --- 所以下一篇的主題大家應該知道了吧~就是:選舉!會講講 ZK 叢集是如何選舉出 Leader。敬請期待吧~ ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303211413353-226974356.gif) 老規矩,如果你有任何對文章中的疑問也可以是建議或者是對 ZK 原理部分的疑問,歡迎來倉庫中提 issue 給我們,或者來語雀話題討論。 > 地址:https://www.yuque.com/kaixin1002/yla8hz 老哥們轉評贊安排一下好嗎,ZKr~ ![](https://img2020.cnblogs.com/blog/759200/202103/759200-20210303213609240-4671316