系列文章git
本篇文章想總結下Raft和ZAB在處理一些一致性問題上的作法,詳見以前對這2個算法的描述github
上述分別是針對以下算法實現的討論:算法
一致性算法在實現狀態機這種應用時,有哪些常見的問題:微信
1 leader選舉學習
1.1 通常的leader選舉過程atom
選舉的輪次.net
選舉出來的leader要包含更多的日誌設計
1.2 leader選舉的效率日誌
會不會出現split vote?以及各自的特色是?server
1.3 加入一個已經完成選舉的集羣
怎麼發現已完成選舉的leader?
加入過程是否對leader處理請求的過程形成阻塞?
1.4 leader選舉的觸發
誰在負責檢測須要進入leader選舉?
2 上一輪次的leader
3 請求處理流程
3.1 請求處理的通常流程
3.2 日誌的連續性問題
3.3 如何保證順序
3.3.1 正常同步過程的順序
3.3.2 異常過程的順序
follower掛掉又鏈接
leader更換
3.4 請求處理流程的異常
4 分區的處理
下面分別來看看Raft和ZooKeeper怎麼來解決的
爲何要進行leader選舉?
在實現一致性的方案,能夠像base-paxos那樣不須要leader選舉,這種方案達成一件事情的一致性還好,面對多件事情的一致性就比較複雜了,因此經過選舉出一個leader來簡化實現的複雜性。
更多的有2個要素:
1.1.1 選舉投票可能會屢次輪番上演,爲了區分,因此須要定義你的投票是屬於哪一個輪次的。
他們都須要在某個輪次內達成過半投票來結束選舉過程
1.1.2 投票PK的過程,更多的是日誌越新越多者獲勝
在選舉leader的時候,一般都但願
選舉出來的leader至少包含以前所有已提交的日誌
天然想到包含的日誌越新越大那就越好。
一般都是比較最後一個日誌記錄,如何來定義最後一個日誌記錄?
有2種選擇,一種就是全部日誌中的最後一個日誌,另外一種就是全部已提交中的最後一個日誌。目前Raft和ZooKeeper都是採用前一種方式。日誌的越新越大表示:輪次新的優先,而後纔是同一輪次下日誌記錄大的優先
Raft:term大的優先,而後entry的index大的優先
ZooKeeper:peerEpoch大的優先,而後zxid大的優先
ZooKeeper有2個輪次,一個是選舉輪次electionEpoch,另外一個是日誌的輪次peerEpoch(即表示這個日誌是哪一個輪次產生的)。而Raft則是隻有一個輪次,至關於日誌輪次和選舉輪次共用了。至於ZooKeeper爲何要把這2個輪次分開,這個稍後再細究,有興趣的能夠一塊兒研究。
可是有一個問題就是:經過上述的日誌越新越大的比較方式能達到咱們的上述但願嗎?
特殊狀況下是不能的,這個特殊狀況詳細見上述給出Raft算法賞析的這一部分
這個案例就是這種比較方式會選舉出來的leader可能並不包含已經提交的日誌,而Raft的作法則是對於日誌的提交多加一個限制條件,即不能直接提交以前term的已過半的entry,即把這一部分的日誌限制成未提交的日誌,從而來實現上述的但願。
ZooKeeper呢?會不會出現這種狀況?又是如何處理的?
ZooKeeper是不會出現這種狀況的,由於ZooKeeper在每次leader選舉完成以後,都會進行數據之間的同步糾正,因此每個輪次,你們都日誌內容都是統一的
而Raft在leader選舉完成以後沒有這個同步過程,而是靠以後的AppendEntries RPC請求的一致性檢查來實現糾正過程,則就會出現上述案例中隔了幾個輪次還不統一的現象
Raft中的每一個server在某個term輪次內只能投一次票,哪一個candidate先請求投票誰就可能先得到投票,這樣就可能形成split vote,即各個candidate都沒有收到過半的投票,Raft經過candidate設置不一樣的超時時間,來快速解決這個問題,使得先超時的candidate(在其餘人還未超時時)優先請求來得到過半投票
ZooKeeper中的每一個server,在某個electionEpoch輪次內,能夠投屢次票,只要遇到更大的票就更新,而後分發新的投票給全部人。這種狀況下不存在split vote現象,同時有利於選出含有更新更多的日誌的server,可是選舉時間理論上相對Raft要花費的多。
1.3.1 怎麼發現已完成選舉的leader?
一個server啓動後(該server原本就屬於該集羣的成員配置之一,因此這裏不是新加機器),如何加入一個已經選舉完成的集羣
Raft:比較簡單,該server啓動後,會收到leader的AppendEntries RPC,這時就會從RPC中獲取leader信息,識別到leader,即便該leader是一個老的leader,以後新leader仍然會發送AppendEntries RPC,這時就會接收到新的leader了(由於新leader的term比老leader的term大,因此會更新leader)
ZooKeeper:該server啓動後,會向全部的server發送投票通知,這時候就會收處處於LOOKING、FOLLOWING狀態的server的投票(這種狀態下的投票指向的leader),則該server放棄本身的投票,判斷上述投票是否過半,過半則能夠確認該投票的內容就是新的leader。
1.3.2 加入過程是否阻塞整個請求?
這個其實還要看對日誌的設計是不是連續的
若是是連續的,則leader中只須要保存每一個follower上一次的同步位置,這樣在同步的時候就會自動將以前欠缺的數據補上,不會阻塞整個請求過程
目前Raft的日誌是依靠index來實現連續的
若是不是連續的,則在確認follower和leader當前數據的差別的時候,是須要獲取leader當前數據的讀鎖,禁止這個期間對數據的修改。差別肯定完成以後,釋放讀鎖,容許leader數據被修改,每個修改記錄都要被保存起來,最後一一應用到新加入的follower中。
目前ZooKeeper的日誌zxid並非嚴格連續的,容許有空洞
觸發通常有以下2個時機
server剛開始啓動的時候,觸發leader選舉
leader選舉完成以後,檢測到超時觸發,誰來檢測?
首先看下上一輪次的leader在掛或者失去leader位置以前,會有哪些數據?
一個日誌是否被過半複製,是否被提交,這些信息是由leader才能知曉的,那麼下一個leader該如何來斷定這些日誌呢?
下面分別來看看Raft和ZooKeeper的處理策略:
Raft的保守策略更可能是由於Raft在leader選舉完成以後,沒有同步更新過程來保持和leader一致(在能夠對外處理請求以前的這一同步過程)。而ZooKeeper是有該過程的
這其實就和實現有密切的關係了。
Raft的copycat實現爲:每一個follower開通一個複製數據的RPC接口,誰均可以鏈接並調用該接口,因此Raft須要來阻止上一輪次的leader的調用。每一輪次都會有對應的輪次號,用來進行區分,Raft的輪次號就是term,一旦舊leader對follower發送請求,follower會發現當前請求term小於本身的term,則直接忽略掉該請求,天然就解決了舊leader的干擾問題
ZooKeeper:一旦server進入leader選舉狀態則該follower會關閉與leader之間的鏈接,因此舊leader就沒法發送複製數據的請求到新的follower了,也就沒法形成干擾了
這個過程對比Raft和ZooKeeper基本上是一致的,大體過程都是過半複製
先來看下Raft:
再來看看ZooKeeper:
在須要保證順序性的前提下,在利用一致性算法實現狀態機的時候,究竟是實現連續性日誌好呢仍是實現非連續性日誌好呢?
還有在複製和提交的時候:
其餘有待後續補充
具體順序是什麼?
這個就是先到達leader的請求,先被應用到狀態機。這就須要看正常運行過程、異常出現過程都是怎麼來保證順序的
3.3.1 正常同步過程的順序
3.3.2 異常過程的順序保證
如follower掛掉又重啓的過程:
Raft:重啓以後,因爲leader的AppendEntries RPC調用,識別到leader,leader仍然會按照leader的log進行順序複製,也不用關心在複製期間新的添加的日誌,在下一次同步中自動會同步
ZooKeeper:重啓以後,須要和當前leader數據之間進行差別的肯定,同時期間又有新的請求到來,因此須要暫時獲取leader數據的讀鎖,禁止此期間的數據更改,先將差別的數據先放入隊列,差別肯定完畢以後,還須要將leader中已提交的議案和未提交的議案也所有放入隊列,即ZooKeeper的以下2個集合數據
ConcurrentMap<Long, Proposal> outstandingProposals
Leader擁有的屬性,每當提出一個議案,都會將該議案存放至outstandingProposals,一旦議案被過半認同了,就要提交該議案,則從outstandingProposals中刪除該議案
ConcurrentLinkedQueue<Proposal> toBeApplied
Leader擁有的屬性,每當準備提交一個議案,就會將該議案存放至該列表中,一旦議案應用到ZooKeeper的內存樹中了,而後就能夠將該議案從toBeApplied中刪除
而後再釋放讀鎖,容許leader進行處理寫數據的請求,該請求天然就添加在了上述隊列的後面,從而保證了隊列中的數據是有序的,從而保證發給follower的數據是有序的,follower也是一個個進行確認的,因此對於leader的回覆也是有序的
若是是leader掛了以後,從新選舉出leader,會不會有亂序的問題?
一旦leader發給follower的數據出現超時等異常
目前ZooKeeper和Raft都是過半便可,因此對於分區是容忍的。如5臺機器,分區發生後分紅2部分,一部分3臺,另外一部分2臺,這2部分之間沒法相互通訊
其中,含有3臺的那部分,仍然能夠湊成一個過半,仍然能夠對外提供服務,可是它不容許有server再掛了,一旦再掛一臺則就所有不可用了。
含有2臺的那部分,則沒法提供服務,即只要鏈接的是這2臺機器,都沒法執行相關請求。
因此ZooKeeper和Raft在一旦分區發生的狀況下是是犧牲了高可用來保證一致性,即CAP理論中的CP。可是在沒有分區發生的狀況下既能保證高可用又能保證一致性,因此更想說的是所謂的CAP兩者取其一,並非說該系統一直保持CA或者CP或者AP,而是一個會變化的過程。在沒有分區出現的狀況下,既能夠保證C又能夠保證A,在分區出現的狀況下,那就須要從C和A中選擇同樣。ZooKeeper和Raft則都是選擇了C。
歡迎關注微信公衆號:乒乓狂魔