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

閱讀時參考的版本是3.3.3.html

簡單的說一下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」.spa

展開來講,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集羣的工做原理,後面將分別展開來討論.code

 



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

 

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

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宕機以後的處理等等,後面再談.

 



三、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狀態,從新開始進行選舉.

相關文章
相關標籤/搜索