zookeeper的領導者選舉和原子廣播


目錄:
     1、工作原理概述
    2、Fast Leader選舉算法(領導者選舉)
    3、Leader與Follower同步數據(原子廣播)


1、工作原理概述

zookeeper3.3.3源碼分析(一)工作原理概述

閱讀時參考的版本是3.3.3.

簡單的說一下zookeeper工作的過程,如果對這個過程還不太清楚,或者說對它如何使用等不太清楚的,可以參考一下其他的文章,比如這篇,這一系列的文章將不講解它如何使用(實際上我也沒有在具體項目中使用過,只是簡單的配置運行起來大概曉得如何工作而已).

zookeeper有兩種工作的模式,一種是單機方式,另一種是集群方式.單機方式不屬於這里分析的范疇,因為研究zookeeper的目的就在於研究一個zookeeper集群的機器如何協調起來工作的.

要配置幾台zookeeper一起工作,大家在開始必須使用相同的配置文件,配置文件中有一些配置項,但是與集群相關的是這一項:

server.1=192.168.211.1:2888:3888
server.2=192.168.211.2:2888:3888

這里定義了兩台服務器的配置,格式為:

server.serverid=serverhost:leader_listent_port:quorum_port

顧名思義,serverid是本服務器的id,leader_listen_port是該服務器一旦成為leader之后需要監聽的端口,用於接收來自follower的請求,quorum_port是集群中的每一個服務器在最開始選舉leader時監聽的端口,用於服務器互相之間通信選舉leader.

需要注意的是,server id並沒有寫在這個配置文件中,而是在datadir中的myid文件中指定,我理解這么做的目的是:所有的服務器統一使用一個配置文件,該配置文件里面沒有任何與特定服務器相關的信息,這樣便於發布服務的時候不會出錯,而獨立出來一個文件專門存放這個server id值.

zookeeper集群工作的過程包括如下幾步:
1) recovery,這個過程泛指集群服務器的啟動和恢復,因為恢復也可以理解為另一種層面上的”啟動”–需要恢復歷史數據的啟動,后面會詳細講解.
2) broadcast,這是啟動完畢之后,集群中的服務器開始接收客戶端的連接一起工作的過程,如果客戶端有修改數據的改動,那么一定會由leader廣播給follower,所以稱為”broadcast”.

展開來說,zookeeper集群大概是這樣工作的:
1) 首先每個服務器讀取配置文件和數據文件,根據serverid知道本機對應的配置(就是前面那些地址和端口),並且將歷史數據加載進內存中.
2) 集群中的服務器開始根據前面給出的quorum port監聽集群中其他服務器的請求,並且把自己選舉的leader也通知其他服務器,來來往往幾回,選舉出集群的一個leader.
3) 選舉完leader其實還不算是真正意義上的”leader”,因為到了這里leader還需要與集群中的其他服務器同步數據,如果這一步出錯,將返回2)中重新選舉leader.在leader選舉完畢之后,集群中的其他服務器稱為”follower”,也就是都要聽從leader的指令.
4) 到了這里,集群中的所有服務器,不論是leader還是follower,大家的數據都是一致的了,可以開始接收客戶端的連接了.如果是讀類型的請求,那么直接返回就是了,因為並不改變數據;否則,都要向leader匯報,如何通知leader呢?就是通過前面講到的leader_listen_port.leader收到這個修改數據的請求之后,將會廣播給集群中其他follower,當超過一半數量的follower有了回復,那么就相當於這個修改操作哦了,這時leader可以告訴之前的那台服務器可以給客戶端一個回應了.
可以看到,上面1),2),3)對應的recovery過程,而4)對應的broadcast過程.

這里只是簡單的描述了一下zookeeper集群的工作原理,后面將分別展開來討論.

 



2、Fast Leader選舉算法(領導者選舉)

 

link:http://www.codedump.info/?p=210

link:http://www.codedump.info/?p=224

 

如何在zookeeper集群中選舉出一個leader,zookeeper使用了三種算法,具體使用哪種算法,在配置文件中是可以配置的,對應的配置項是”electionAlg”,其中1對應的是LeaderElection算法,2對應的是AuthFastLeaderElection算法,3對應的是FastLeaderElection算法.默認使用FastLeaderElection算法.其他兩種算法我沒有研究過,就不多說了.

要理解這個算法,最好需要一些paxos算法的理論基礎.

1) 數據恢復階段
首先,每個在zookeeper服務器先讀取當前保存在磁盤的數據,zookeeper中的每份數據,都有一個對應的id值,這個值是依次遞增的,換言之,越新的數據,對應的ID值就越大.

2) 向其他節點發送投票值
在讀取數據完畢之后,每個zookeeper服務器發送自己選舉的leader(首次選自己),這個協議中包含了以下幾部分的數據:
    a)所選舉leader的id(就是配置文件中寫好的每個服務器的id) ,在初始階段,每台服務器的這個值都是自己服務器的id,也就是它們都選舉自己為leader.
    b) 服務器最大數據的id,這個值大的服務器,說明存放了更新的數據.
    c)邏輯時鍾的值,這個值從0開始遞增,每次選舉對應一個值,也就是說:  如果在同一次選舉中,那么這個值應該是一致的 ;  邏輯時鍾值越大,說明這一次選舉leader的進程更新.
    d) 本機在當前選舉過程中的狀態,有以下幾種:LOOKING,FOLLOWING,OBSERVING,LEADING,顧名思義不必解釋了吧.

3)接受來自其他節點的數據

每台服務器將自己服務器的以上數據發送到集群中的其他服務器之后,同樣的也需要接收來自其他服務器的數據,它將做以下的處理:
(1)如果所接收數據中服務器的狀態還是在選舉階段(LOOKING 狀態),那么首先判斷邏輯時鍾值,又分為以下三種情況:
     a) 如果發送過來的邏輯時鍾大於目前的邏輯時鍾,那么說明這是更新的一次選舉,此時需要更新一下本機的邏輯時鍾值,同時將之前收集到的來自其他服務器的選舉清空,因為這些數據已經不再有效了.然后判斷是否需要更新當前自己的選舉情況.在這里是根據選舉leader id,保存的最大數據id來進行判斷的,這兩種數據之間對這個選舉結果的影響的權重關系是:首先看數據id,數據id大者勝出;其次再判斷leader id,leader id大者勝出.然后再將自身最新的選舉結果(也就是上面提到的三種數據)廣播給其他服務器).

    b) 發送過來數據的邏輯時鍾小於本機的邏輯時鍾,說明對方在一個相對較早的選舉進程中,這里只需要將本機的數據發送過去就是了

    c) 兩邊的邏輯時鍾相同,此時也只是調用totalOrderPredicate函數判斷是否需要更新本機的數據,如果更新了再將自己最新的選舉結果廣播出去就是了.

然后再處理兩種情況:
    1)服務器判斷是不是已經收集到了所有服務器的選舉狀態,如果是,那么這台服務器選舉的leader就定下來了,然后根據選舉結果設置自己的角色(FOLLOWING還是LEADER),然后退出選舉過程就是了.
    2)即使沒有收集到所有服務器的選舉狀態,也可以根據該節點上選擇的最新的leader是不是得到了超過半數以上服務器的支持,如果是,那么當前線程將被阻塞等待一段時間(這個時間在finalizeWait定義)看看是不是還會收到當前leader的數據更優的leader,如果經過一段時間還沒有這個新的leader提出來,那么這台服務器最終的leader就確定了,否則進行下一次選舉. 

(2) 如果所接收服務器不在選舉狀態,也就是在FOLLOWING或者LEADING狀態

做以下兩個判斷:
    a) 如果邏輯時鍾相同,將該數據保存到recvset,如果所接收服務器宣稱自己是leader,那么將判斷是不是有半數以上的服務器選舉它,如果是則設置選舉狀態退出選舉過程
    b) 否則這是一條與當前邏輯時鍾不符合的消息,那么說明在另一個選舉過程中已經有了選舉結果,於是將該選舉結果加入到outofelection集合中,再根據outofelection來判斷是否可以結束選舉,如果可以也是保存邏輯時鍾,設置選舉狀態,退出選舉過程.
代碼如下:

以一個簡單的例子來說明整個選舉的過程.
假設有五台服務器組成的zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數據,在存放數據量這一點上,都是一樣的.假設這些服務器依序啟動,來看看會發生什么.
1) 服務器1啟動,此時只有它一台服務器啟動了,它發出去的報沒有任何響應,所以它的選舉狀態一直是LOOKING狀態
2) 服務器2啟動,它與最開始啟動的服務器1進行通信,互相交換自己的選舉結果,由於兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由於沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1,2還是繼續保持LOOKING狀態.
3) 服務器3啟動,根據前面的理論分析,服務器3成為服務器1,2,3中的老大,而與上面不同的是,此時有三台服務器選舉了它,所以它成為了這次選舉的leader.
4) 服務器4啟動,根據前面的分析,理論上服務器4應該是服務器1,2,3,4中最大的,但是由於前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了.
5) 服務器5啟動,同4一樣,當小弟.

以上就是fastleader算法的簡要分析,還有一些異常情況的處理,比如某台服務器宕機之后的處理,當leader宕機之后的處理等等,后面再談.

 



3、Leader與Follower同步數據(原子廣播)

 

 

      根據 Fast Leader選舉算法中的分析,如果一台zookeeper服務器成為集群中的leader,那么一定是當前所有服務器中保存數據最多(不是最新??)的服務器,所以在這台服務器成為leader之后,首先要做的事情就是與集群中的其它服務器(現在是follower)同步數據,保證大家的數據一致,這個過程完畢了才開始正式處理來自客戶端的連接請求.

      Fast Leader選舉算法中提到的同步數據時使用的邏輯時鍾,它的初始值是0,每次選舉過程都會遞增的,在leader正式上任之后做的第一件事情,就是根據當前保存的數據id值,設置最新的邏輯時鍾值。

    隨后,leader構建NEWLEADER封包,該封包的數據是當前最大數據的id,廣播給所有的follower,也就是告知follower leader保存的數據id是多少,大家看看是不是需要同步。然后,leader根據follower數量給每個follower創建一個線程LearnerHandler,專門負責接收它們的同步數據請求.leader主線程開始阻塞在這里,等待其他follower的回應(也就是LearnerHandler線程的處理結果),同樣的,只有在超過半數的follower已經同步數據完畢,這個過程才能結束,leader才能正式成為leader.

leader所做的工作:

所以其實leader與follower同步數據的大部分操作都在LearnerHandler線程中處理的,接着看這一塊.
leader接收到的來自某個follower封包一定是FOLLOWERINFO,該封包告知了該服務器保存的數據id.之后根據這個數據id與本機保存的數據進行比較:
1) 如果數據完全一致,則發送DIFF封包告知follower當前數據就是最新的了.
2) 判斷這一階段之內有沒有已經被提交的提議值,如果有,那么:
    a) 如果有部分數據沒有同步,那么會發送DIFF封包將有差異的數據同步過去.同時將follower沒有的數據逐個發送COMMIT封包給follower要求記錄下來.
    b) 如果follower數據id更大,那么會發送TRUNC封包告知截除多余數據.(一台leader數據沒同步就宕掉了,選舉之后恢復了,數據比現在leader更新)
3) 如果這一階段內沒有提交的提議值,直接發送SNAP封包將快照同步發送給follower.
4)消息完畢之后,發送UPTODATE封包告知follower當前數據就是最新的了,再次發送NEWLEADER封包宣稱自己是leader,等待follower的響應.

follower做的工作:
(1)會嘗試與leader建立連接,這里有一個機制,如果一定時間內沒有連接上,就報錯退出,重新回到選舉狀態.
(2)其次在發送FOLLOWERINFO封包,該封包中帶上自己的最大數據id,也就是會告知leader本機保存的最大數據id.
(3)根據前面對LeaderHandler的分析,leader會根據不同的情況發送DIFF,UPTODATE,TRUNC,SNAP,依次進行處理就是了,此時follower跟leader的數據也就同步上了.
(4)由於leader端發送的最后一個封包是UPTODATE,因此在接收到這個封包之后follower結束同步數據過程,發送ACK封包回復leader.

以上過程中,任何情況出現的錯誤,服務器將自動將選舉狀態切換到LOOKING狀態,重新開始進行選舉.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM