【4.分佈式存儲】-共識算法【精】paxos/mutil-p/fast-p/raft/bully/zab

本文講分佈式的共識概念,算法,應用。涉及到raft,paxos(basic,multi,fast),zk,etcd,chubby。以及思考。關注與如何共識,對於zk,etcd,chubby的接口如何應用於服務發現,分佈式鎖等略過。html

共識:一個或多個節點提出提案,算法選擇一個值。一致贊成,完整(只決定一次),有效(節點投票是有效值,是被提議的值),終止(宕機不回來)。
paxos徹底符合,但raft,zap考慮的是宕機還會回來的狀況,用日誌保證。能解決上篇(https://segmentfault.com/a/11...)中諸如如下問題:
全序廣播至關於重複多倫共識:raft和zap等直接實現全序廣播,mutil-paxos也是,都有leader,只決定值的順序。有固定leader比每一個節點寫入,leader維持全序,其餘節點同步,衝突少(若沒有leader paxos那種要先同步一遍獲取最大值,再投票,多了一次pre),畫一個有領導和無領導每一個帶序號的圖能夠很好理解帶leader會容易些,但單leader有瓶頸。
單領導類的共識:1選出一位領導者,2對領導者的提議進行表決(防止1,一個節點相信本身是領導)投票是同步的,動態成員擴展難,依靠超時檢測節點失效,若只有一條特定網絡不可靠,會進入領導頻繁二人轉局面。有領導後也不能領導決定,防止從新恢復等多領導定的狀況。
缺點:要多數都贊成,很慢。基本動態擴容很難,手動好比etcdnode

共識算法

raft

  • 共識過程
    數據一致性是經過日誌複製的方式,client發給leader(寫只發給leader,follower備份恢復用),leader寫入日誌,同步給follower,當多數follower寫入日誌並返回給leader時,leader提交數據,返回給客戶端確認消息, 發給follower數據已提交,follower提交數據,發回確認給leader。全部的發送都隨着跳頻發過去。raft中全部server之間的通訊都是RPC調用,而且只有兩種類型的RPC調用:第一種是RequestVote,用於選舉leader;第二種是AppendEntries。日誌和投票結果都須要持續化寫在磁盤中,保證宕機後重啓任然正常。
    clipboard.png
  • leader選舉
    leader(有任期字段term),candidate, follower.每一個節點有在T到2T之間隨機選擇超時時間。leader和follower經過跳頻聯繫。當一個follower收不到leader的跳頻超時時將發起投本身的票。任何一個follower只能投一票,若是發現本身的日誌比請求中攜帶的更新,則拒絕投票。當一輪投票結束有多個候選者時,這幾個候選者從新分配隨機的超時時間
  • leader同步日誌給follower
    在上述數據共識中,當leader確認提交數據後,leader會一直不斷地重試提交的rpc給follower、重試,直到請求成功;即便follower宕機了,重啓後leader仍會接着發請求,直到請求成功,當leader宕機,如何向follower繼續發;1.leader的日誌只能增長,=》因此在選擇時選term大,log長的 2.leader會把本身的log複製到其餘機器,若是新達到多數而且此任期內已有數據過半(掛前的一次數據不會被重複提交)就提交,只提交新任期的,同步follower仍是要同步。
  • log一致性
    每一個日誌entry:iterm+index.每次發送AppendEntries時須要帶上一次的,follower檢查是否同樣,同樣才接受來保證全部機器log一致
    leader從新選出,爲了恢復log一致性,leader爲集羣中全部follower都保存一個狀態變量,即nextIndex:1)nextIndex是leader準備向某個follower發送的下一個log entry的index;2)當leader剛剛即位後,nextIndex的初始值是(1+leader's last index);
    當leader看到請求被拒絕時,其動做很是簡單:只需將nextIndex-1,再次嘗試。
  • 須要持久化term和投票
    term須要存盤
    任意一個server在一個term內只能投出一票;一旦已經投給了一個candidate,它必須拒絕其餘candidate的投票請求;其實server根本不在乎把票投給誰,它只會把票投給最早到請求到它的candidate;爲了保證這一點,必須把投票信息持久保存到磁盤上,這樣能夠保證即便該server投完票後宕機,稍後又當即重啓了,也不會在同一個term內給第二個candidate投票了。

paxos

  • basic paxos
    第一階段收集最新的N和V,第二階段發起提議:
    clipboard.png
    實際上這裏的proposal是leader。共識算法正常是proposor,leader,accepter,leaner(先忽略),用來決議proposer的提議號和是否成功的。每次proposal先到leader(可隨機選取,不重要),leader發給accepter若沒有衝突返回any不然返回已選的,繼續上述過程。
    問題:多個Proposal可能出現死鎖一直循環遞增N的狀況:

    clipboard.png

    上面這個是https://www.microsoft.com/en-...。爲了方便理解,去除了實現細節。實時應用中,客戶端不會本身處理衝突+1再次投票和發送給其餘leaner,這些應該由另外一個角色,在basic中,由一羣c協調者,能夠和acceptor同樣,或者是其中的部分構成,每輪隨機一個c做爲leader,負責收集本輪結果和通知leaner。proposal->leader(每一個client隨機發就能夠做爲本輪leader)->pre->acceptors返回最大N的值V->帶N請求->acceptors->leader->返回給proposal->client失敗或者成功或再次投票->投票成功後發給leaner。此過程當中CLIENT2再次發送是另外一個leader。算法

  • fast paxos
    若proposal和acceptor,leader,leaner都是分佈式,且要持久化,持久化+發送來回的代價就多了,
    若leader發現沒有衝突,再也不參與,proposal直接提交給acceptor(同一輪只投給先到的),直接發送給leaner,能夠理解爲基於樂觀鎖的思想,leaner和CLIENT都自行決議,
    若proposal沒有決策成功(先到的就是投票,沒有半數以上的),1.從新引入leader,異步發送給協調者,協調者選擇(由於acceptor只投一次),發給proposal結果(再次引入leader)。2.無leader,在acceptor決議後發送給全部acceptor,其餘acceptor收到此消息後對i+1輪的能夠比較投票(即便同時刻一個一半也能夠再比較投一次,這兩種的比較都複雜,要看各個提議的acceptor集合,這部分看論文吧)。
    https://www.microsoft.com/en-...
  • muti-paxos
    當leader穩定,能夠省去prepare階段。簡單的說用一個序號來標識是否leader穩定(和raft,zk是同樣的,轉共識爲全序序號的過程),若穩定更新序號直接發送給acceptor,acceptor須要記錄序號,若發現有index>當前則返回false從新prepare。由於沒有prepare,不知道每次最大的n,不知道leader是否穩定加入了全序。在prepare時並不須要此過程。
    具體作法以下:

    clipboard.png

  chubby就是一個典型的Muti-Paxos算法應用,在Master穩定運行的狀況下,只須要使用同一個編號來依次執行每個Instance的Promise->Accept階段處理。數據庫

raft/paxos/zap/mutli-paxos區別

  • raft要有一個leader。在選主時每一個follower只能投一次,不成功隨機時間下一次。有主時的共識由主來給日誌編號,follower比較就好,follower保證穩定可替換便可。
  • mutli-paxos在去掉pre後和raft類似,都是日誌記錄,區別是mp容許任何acceptor升級爲leader,raft則很嚴格好比只有最完整日誌的才行,mp在preprare後會知道當前最大的index,對於舊的異步補空洞。raft以爲補空洞過程太繁瑣,增長了選主的複雜度。
  • paxos leader不能那麼重要(fast paxos在無衝突時甚至無leader參與),每次能夠隨機選,只是彙總投票。paxos在fast模式下,衝突處理時,每一個acceptor能夠更新選票從新投(實際上是衝突的解決,也能夠不算投票,根據集合等複雜的邏輯,在zk中就按照現有集合票數)。
  • zap仍是有leader的。zap在無主的時候選舉算法和fast paxos很像,有最大xid(相似pre階段,只不過是上次存好的),先選主,每次選主的提案直接給acceptor而且採用無協調者的衝突處理。在有主時,用paxos的思想先pre收集並同步信息保證一致,主處理寫,多數處理成功後回覆。
  • paxos優點就是單主能不能抗住了,單主投票只能一次一個。

zookeeper

zk定位於分佈式協調服務
官方:https://zookeeper.apache.org/...
下面介紹zk的經常使用功能,架構,共識過程。apache

架構

自己的數據組織以文件形式,每一個葉節點znodes,非頁節點只是命名空間的路徑標識;可是存儲於內存,記錄磁盤日誌,副本包含完整內存數據和日誌,znodes維護節點的版本,zxid等全部信息。
Zookeeper對於每一個節點QuorumPeer的設計至關的靈活,QuorumPeer主要包括四個組件:客戶端請求接收器(ServerCnxnFactory)、數據引擎(ZKDatabase)、選舉器(Election)、核心功能組件(Leader/Follower/Observer不一樣)

做用

1.單獨zk集羣元數據的可靠性和一致性保證,元數據保存在zk全部副本中(少許徹底能夠放在內存中數據)
路由,選擇數據庫,調度程序
2.單獨zk集羣,鎖,防禦令牌,獲取鎖或者zxid
3.變動通知,每一個變動都會發送到全部節點
watch機制
4.用於檢測,服務發現
session:
每一個ZooKeeper客戶端的配置中都包括集合體中服務器的列表。在啓動時,客戶端會嘗試鏈接到列表中的一臺服務器。若是鏈接失敗,它會嘗試鏈接另外一臺服務器,以此類推,直到成功與一臺服務器創建鏈接或由於全部ZooKeeper服務器都不可用而失敗。
只要一個會話空閒超過必定時間,均可以經過客戶端發送ping請求(也稱爲心跳)保持會話不過時。ping請求由ZooKeeper的客戶端庫自動發送,所以在咱們的代碼中不須要考慮如何維護會話。這個時間長度的設置應當足夠低,以便能檔檢測出服務器故障(由讀超時體現),而且可以在會話超時的時間段內從新蓮接到另一臺服務器。segmentfault

zookeeper數據同步過程

採用了遞增的事務id號(zxid)來標識事務。全部的提議(proposal)都在被提出的時候加上了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個新的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。服務器

  • zab protocol網絡

    Leader election
        leader選舉過程,electionEpoch自增,在選舉的時候lastProcessedZxid越大,越有可能成爲leader
    Discovery:
        第一:leader收集follower的lastProcessedZxid,這個主要用來經過和leader的lastProcessedZxid對比來確認follower須要同步的數據範圍
        第二:選舉出一個新的peerEpoch,主要用於防止舊的leader來進行提交操做(舊leader向follower發送命令的時候,follower發現zxid所在的peerEpoch比如今的小,則直接拒絕,防止出現不一致性)
    Synchronization:
        follower中的事務日誌和leader保持一致的過程,就是依據follower和leader之間的lastProcessedZxid進行,follower多的話則刪除掉多餘部分,follower少的話則補充,一旦對應不上則follower刪除掉對不上的zxid及其以後的部分而後再從leader同步該部分以後的數據
    Broadcast
        正常處理客戶端請求的過程。leader針對客戶端的事務請求,而後提出一個議案,發給全部的follower,一旦過半的follower回覆OK的話,leader就能夠將該議案進行提交了,向全部follower發送提交該議案的請求,leader同時返回OK響應給客戶端

實際上zookeeper中算法三階段:FSE=>Recovery=>Broadcast(廣播和上面的一致)session

  • fast leader election
    基於fast paxos。發送給全部的節點。沒有隨機leader參與收集。
    clipboard.png架構

    LOOKING:進入leader選舉狀態
    FOLLOWING:leader選舉結束,進入follower狀態
    LEADING:leader選舉結束,進入leader狀態
    OBSERVING:處於觀察者狀態
    1.serverA首先將electionEpoch自增,而後爲本身投票
    2 serverB接收到上述通知,而後進行投票PK
    若是serverB收到的通知中的electionEpoch比本身的大,則serverB更新本身的electionEpoch爲serverA的electionEpoch
    若是該serverB收到的通知中的electionEpoch比本身的小,則serverB向serverA發送一個通知,將serverB本身的投票以及electionEpoch發送給serverA,serverA收到後就會更新本身的electionEpoch
    在electionEpoch達成一致後,就開始進行投票之間的pk,優先比較proposedEpoch,而後優先比較proposedZxid,最後優先比較proposedLeader
    pk完畢後,若是本機器投票被pk掉,則更新投票信息爲對方投票信息,同時從新發送該投票信息給全部的server。若是本機器投票沒有被pk掉,若是是looking,過半更改狀態,若是FOLLOWING/LEADING說明落後,加速收斂
  • Recovery
    略:https://my.oschina.net/pingpa...
  • follower讀寫過程圖:

clipboard.png

ectd

常常應用於配置共享和服務發現,相比於zk,簡單。使用 Go 語言編寫部署簡單;使用 HTTP 做爲接口使用簡單;使用 `Raft 算法`保證強一致性讓用戶易於理解。無需安裝客戶端。提供接口K-V存儲(storing up to a few GB of data with consistent ordering,提供線性讀),watch,lease,lock,election。由於共識徹底實現的raft因此只簡單說下部署模式,節點組成,數據持久化等。
官方:https://coreos.com/etcd/docs/latest/

架構圖:

單節點以下
clipboard.png
store:爲用戶提供API
集羣會區分proxy,leader,follower

  • 啓動
    etcd 有三種集羣化啓動的配置方案,分別爲靜態配置啓動、etcd 自身服務發現、經過 DNS 進行服務發現
    自身服務發現:
    首先就用自身單個的 url 構成一個集羣,而後在啓動的過程當中根據參數進入discovery/discovery.go源碼的JoinCluster函數。由於咱們事先是知道啓動時使用的 etcd 的 token 地址的,裏面包含了集羣大小 (size) 信息。在這個過程實際上是個不斷監測與等待的過程。啓動的第一步就是在這個 etcd 的 token 目錄下注冊自身的信息,而後再監測 token 目錄下全部節點的數量,若是數量沒有達標,則循環等待。當數量達到要求時,才結束,進入正常的啓動過程。
  • 運行
    proxy、leader\follower。proxy只負責轉發,etcd proxy支持2種運行模式:readwrite和readonly,缺省的是readwrite,即proxy會將全部的讀寫請求都轉發給etcd集羣;readonly模式下,只轉發讀請求,寫請求將會返回http 501錯誤。proxy保證參與投票數量有限的性能,全部follower都同步完數據才返回成功(由於異常不自動回來,能夠所有,已經被管理員補齊了,不然只能讀主,所以節點不能不少),在正常節點故障後,能夠由管理員手動處理,一個備份的功能。
    etcd 能夠代理訪問 leader 節點的請求,因此若是你能夠訪問任何一個 etcd 節點,那麼你就能夠無視網絡的拓撲結構對整個集羣進行讀寫操做,不然只能鏈接leader.
    無端障自恢復(容易出錯,考慮到已經高可用,管理員有時間自行恢復)
  • 數據持久:WAL+snapshot(刪除WAL)
    從 snapshot 中得到集羣的配置信息,包括 token、其餘節點的信息等等,而後載入 WAL 目錄的內容,從小到大進行排序。根據 snapshot 中獲得的 term 和 index,找到 WAL 緊接着 snapshot 下一條的記錄,而後向後更新,直到全部 WAL 包的 entry 都已經遍歷完畢,Entry 記錄到 ents 變量中存儲在內存裏。此時 WAL 就進入 append(read和append互斥) 模式,爲數據項添加進行準備。
    當 WAL 文件中數據項內容過大達到設定值(默認爲 10000)時,會進行 WAL 的切分,同時進行 snapshot 操做。這個過程能夠在etcdserver/server.go的snapshot函數中看到。因此,實際上數據目錄中有用的 snapshot 和 WAL 文件各只有一個,默認狀況下 etcd 會各保留 5 個歷史文件
  • 數據KV
    內存BTREE索引,物理B+樹,存歷史版本,沒有引用後壓縮刪除歷史版本。
  • 應用:https://www.infoq.cn/article/...

chubby

GFS和Big Table等大型系統都用他來解決分佈式協做、元數據存儲和Master選擇等一系列與分佈式鎖服務相關的問題
客戶端發送到其餘機器都會將master反饋,從新轉到master,持續直到換。
數據組織方式和zk同樣。
只有主節點提供讀寫(數據和日誌有空洞),高可靠和可用,吞吐量不如zk。在換主階段會阻塞。

etcd,chubby,zk的對比

由於zk,etcd都會補齊follower。所以主從均可以讀。etcd的主是固定的,除非故障=》raft的換主。chubby(06年)用過的mutil-poxas主通常不變,不保證每輪主從數據一致,只有主有讀寫能力,吞吐量會差一些,一萬臺機器分佈式鎖仍是能夠的。etcd(14年)是後來的,確定更好啊,有http接口,一切都更輕量簡單,缺點只是無端障自恢復吧,zk每次都會選主(但基於一個xid,基本也相似mutil-poxas會穩定),可自動恢復。

相關文章
相關標籤/搜索