分布式一致性算法Raft简介(上)
最近看了Ongaro在2014年的博士論文《CONSENSUS: BRIDGING THEORY AND PRACTICE》的部分章節(jié),對(duì)raft有了初步的理解。其中論文中提到用于教學(xué)的user study,個(gè)人感覺非常不錯(cuò),言簡(jiǎn)意賅,特此分享出來。本文基本與原講解一致,又加上了筆者的一點(diǎn)理解。
資源來源于Ongaro和Ousterhout在youtube上的分享(http://youtu.be/YbZ3zDzDnrw),共有31個(gè)slide,因篇幅字?jǐn)?shù)限制分為上下:
slide 1:
John Ousterhout,對(duì)分布式一致性算法Raft進(jìn)行簡(jiǎn)單介紹。
slide 2:
?
Raft的總體目標(biāo)是將log完全一樣地復(fù)制到集群中的所有機(jī)器上,用來創(chuàng)建所謂的Replicated State Machine(多副本狀態(tài)機(jī),就是具有多個(gè)copy的應(yīng)用程序)。
設(shè)想你有一個(gè)程序或應(yīng)用,你想讓它非常可靠,其中一個(gè)辦法就是,在多個(gè)機(jī)器上同時(shí)運(yùn)行這同一個(gè)程序,并且保證完全一樣地運(yùn)行,這就是replicated state machine的思想。state machine是個(gè)抽象概念,就是指一個(gè)程序或應(yīng)用,能接收請(qǐng)求,并能輸出結(jié)果(可以理解為有輸入能輸出的程序黑盒子)。(注意state machine指的是一個(gè)具體的應(yīng)用程序,不是通常理解的機(jī)器;server才是指的通常的機(jī)器或?qū)嵗?#xff09;
引入log這個(gè)概念,有助于使這些state machines執(zhí)行完全一樣的命令。下面解釋下運(yùn)作過程:如果一個(gè)客戶端,想執(zhí)行一個(gè)command(命令,指令,指的是具體的某個(gè)操作請(qǐng)求),那么它可以請(qǐng)求其中一臺(tái)state machine,這臺(tái)machine就把這個(gè)命令,如command X,記錄到自己的本地日志log中,另外還要把command X傳遞給其他所有machines;其他machine也在各自的log中記錄下這條命令;一旦這條command X被safely replicated(安全地復(fù)制)到所有machine的所有l(wèi)og中,那么這個(gè)command X就可以被傳遞給各個(gè)machine開始執(zhí)行,一旦有machine執(zhí)行完成命令X,就會(huì)把結(jié)果返回給客戶端。
(大家注意這個(gè)過程:收到請(qǐng)求command->記錄本地log->將log復(fù)制到其他machine->一旦認(rèn)為log被安全地復(fù)制完成->才允許各個(gè)machine開始執(zhí)行command->一旦有machine執(zhí)行完成command->即把command的執(zhí)行結(jié)果返回給客戶端。這也就意味著不是所有請(qǐng)求command都能被最終執(zhí)行,只有那些“安全地復(fù)制”完成的,才允許執(zhí)行。特別注意理解什么叫安全地復(fù)制完成,直覺理解就是只要復(fù)制完成,就是最終狀態(tài),不允許再反悔再更改;我理解是兩點(diǎn),一是復(fù)制必須持久化,如寫磁盤;二是必須復(fù)制到集群中大多數(shù)machine,得到大多數(shù)的承認(rèn);)
所以可以看到,只要所有l(wèi)og和machine都完全一樣,且所有不同server(機(jī)器)上的所有state machine都按照完全一樣的順序執(zhí)行l(wèi)og中的相同命令,那么所有machine必定輸出完全一樣的結(jié)果。(大家注意下這個(gè)推理過程:1 所有機(jī)器上都部署相同的machine應(yīng)用;2 因?yàn)閘og是復(fù)制過去的,安全地復(fù)制保證了所有機(jī)器上的所有machine的log都完全一樣;3 所有機(jī)器上的所有machine都按照log中的相同順序執(zhí)行命令;4 log是相同的,log中相同位置的命令也是相同的;5 所有機(jī)器上的所有machine都一樣,執(zhí)行的命令也一樣,輸出的結(jié)果也必定完全一樣。其實(shí)這里還有個(gè)隱含條件,就是machine應(yīng)用必須是deterministic的,即確定性的,由輸入必定能推出唯一的輸出,不能帶有隨機(jī)性模糊性;其實(shí)跟我們通常理解的應(yīng)用是一致的;)
Consensus Module(一致性模塊,圖中有標(biāo)注,是machine里的一個(gè)協(xié)調(diào)控制模塊)的工作職責(zé)是管理這些logs,確保被合理地復(fù)制,并決定什么時(shí)候?qū)⑦@些logs中的command提交給state machine執(zhí)行(其實(shí)consensus module就是一個(gè)協(xié)調(diào)模塊,相當(dāng)于整個(gè)系統(tǒng)的大腦,是整個(gè)算法的控制核心);之所以叫consensus based算法,是因?yàn)槲覀儾⒉灰笏袡C(jī)器都是一直在線并運(yùn)行良好。事實(shí)上為了保證效率,只要求大多數(shù)機(jī)器在線,并能互相通信即可。(consensus在英文中的本意是共識(shí)、一致意見、大多數(shù)人的意見)比如,一個(gè)集群中3臺(tái)機(jī)器,可以容忍1臺(tái)機(jī)器故障,只要有2臺(tái)在線即可;再比如5臺(tái)機(jī)器的集群,可以容忍2臺(tái)機(jī)器故障,只要3臺(tái)在線即可。這里簡(jiǎn)要說下我們的系統(tǒng)能處理的異常:機(jī)器可以crash,可以停止運(yùn)行,可以暫停運(yùn)行再過段時(shí)間恢復(fù),但要求運(yùn)行的時(shí)候必須正常(意思是你可以罷工,但你在崗的時(shí)候干活兒必須正確);因此我們不能處理Byzantine failures(拜占庭將軍問題,指的是惡意篡改數(shù)據(jù)這類行為);我們也允許網(wǎng)絡(luò)中斷、消息丟失、消息延遲、消息傳遞亂序、網(wǎng)絡(luò)分化等。(意思就是我們正常分布式環(huán)境中會(huì)遇到的網(wǎng)絡(luò)問題、消息丟失延遲等問題,raft都能應(yīng)對(duì);唯一不能處理的就是惡意篡改數(shù)據(jù)這類,比如受到惡意網(wǎng)絡(luò)攻擊、或者你惡意篡改磁盤中的log日志等這類非正常行為)。
slide 3:
?
實(shí)現(xiàn)一致性算法有兩種可能方式:
1)第一種叫對(duì)稱式,或無leader:這種方式中所有server的角色完全一樣,權(quán)利也一樣,在任意時(shí)刻的行為也基本一樣,所有server都是對(duì)等的;客戶端可以請(qǐng)求任意一臺(tái)server,將command寫入log中,并復(fù)制到其他server(其實(shí)就是完全平等制、無政府主義);
2)第二種叫非對(duì)稱式,或leader-based:在任意時(shí)間各個(gè)server都是不平等的,某個(gè)時(shí)刻只有一個(gè)server是leader,管理集群中的所有操作;其他server都是被統(tǒng)治的,只能簡(jiǎn)單按照leader的旨意干活;在這類系統(tǒng)中,客戶端只能與leader通信,也只有l(wèi)eader能與其他server交流(其實(shí)就是專制獨(dú)裁統(tǒng)治,只有一個(gè)leader,可以為所欲為,其他所有人都必須聽命于leader,只有l(wèi)eader能對(duì)外交流);
raft采用的就是第二種leader-based的方式,并且把一致性問題分解為兩個(gè)不同問題:一是在有l(wèi)eader的情況下集群如何正常運(yùn)作,二是leader掛掉之后如何選舉更換leader。raft采用的這種leader-based方式的優(yōu)勢(shì)是使得正常運(yùn)作過程非常簡(jiǎn)單,因?yàn)槟悴恍枰獡?dān)心多個(gè)leader同時(shí)指揮導(dǎo)致的沖突,記住只有一個(gè)leader,leader可以為所欲為;raft算法的所有復(fù)雜性其實(shí)都來自于leader變更,這是因?yàn)榕fleader忽然掛掉,可能導(dǎo)致整個(gè)系統(tǒng)處于不一致的狀態(tài),新leader上任后必須收拾殘局。(后面大家會(huì)有切身體會(huì),raft正常的運(yùn)行過程非常簡(jiǎn)單,但是leader變更過程非常復(fù)雜)
通常來講,有l(wèi)eader的系統(tǒng)比無leader的系統(tǒng)效率更高,原因很簡(jiǎn)單,你不需要擔(dān)心多個(gè)server之間的沖突,你只需要額外處理下leader變更流程;(獨(dú)裁不一定是壞事,效率更高!)
slide 4:
?
raft講解提綱,共6個(gè)部分:
1、leader選舉:如何從多個(gè)server中挑選一臺(tái)作為leader;當(dāng)leader掛掉之后,如何感知到,并挑選出新的leader來替換舊leader;
2、正常運(yùn)行(最基本的log復(fù)制過程):leader從客戶端收到請(qǐng)求后如何將log復(fù)制到其他機(jī)器;這其實(shí)是整個(gè)raft算法中最簡(jiǎn)單的部分;
3、leader變更過程中的安全性和一致性:leader變更是raft中最難的,也是最關(guān)鍵的;首先會(huì)講下safety到底意味著什么,以及如何保證safety;接著講新leader上任后如何處理log,使得整個(gè)系統(tǒng)恢復(fù)一致性狀態(tài);
4、neutralize 舊leader:這是leader變更中的另一個(gè)問題,就是舊leader并沒有真的死掉,死灰復(fù)燃,重新恢復(fù)之后,我們?cè)撊绾翁幚?#xff1b;
5、客戶端交互:客戶端如何與整個(gè)系統(tǒng)交互?關(guān)鍵點(diǎn)是請(qǐng)求過程中server掛掉了client怎么辦?如何實(shí)現(xiàn)linearizable semantics(線性化語義),即每個(gè)客戶端命令只能執(zhí)行一次(once and exactly once,其實(shí)就是防止出現(xiàn)多次執(zhí)行出問題,類似于冪等性概念);
6、配置變更:如何在集群中新增、或刪除機(jī)器?
slide 5:
?
開始講解6個(gè)部分之前,先從整體上說下系統(tǒng)中的server狀態(tài):
在任意時(shí)刻,系統(tǒng)中的任意一個(gè)server,只能是以下3種狀態(tài)中的一種:
1)leader:同一時(shí)刻至多只能由一個(gè)server處于leader狀態(tài),處理所有的客戶端交互、日志復(fù)制等;(同一時(shí)刻至多一個(gè),意思是要么一個(gè)leader,要么沒leader)
2)follower:大部分時(shí)候集群中的絕大部分server都是follower的狀態(tài),這些server是完全被動(dòng)的狀態(tài),即不能主動(dòng)發(fā)出請(qǐng)求,只能響應(yīng)來自其他server的請(qǐng)求;(意思就是不能主動(dòng)問別人,只能別人問了你回答;don't ask me! I ask you, you answer only!)
3)candidate:這是一個(gè)從follower到leader之間的中間狀態(tài);只是leader選舉過程的一個(gè)臨時(shí)狀態(tài);
正常情況下的集群狀態(tài)應(yīng)該是:1個(gè)leader,其他所有都是follower。
上圖的下半部分展示了server狀態(tài)的變遷過程:
1)follower想成為leader,必須先變成candidate,然后發(fā)起選舉投票;如果投票不足,仍回到follower狀態(tài);如果投票過半,變成leader;
2)leader故障恢復(fù)后發(fā)現(xiàn)已經(jīng)有新leader了,則自動(dòng)下臺(tái),進(jìn)入follower狀態(tài);
slide 6:
?
時(shí)間被劃分成一個(gè)個(gè)的term(這里的term,還有zookeeper中的epoch,都是同一個(gè)意思,指的就是任期、時(shí)期、年代;指的是某一個(gè)leader的統(tǒng)治時(shí)期),每個(gè)term都有一個(gè)number,這個(gè)number必須單向遞增且從未被用過;
每個(gè)term時(shí)期,分兩部分,一是為這個(gè)term選舉leader的過程,二是一旦選舉成功,這個(gè)leader在這個(gè)term的剩余時(shí)間內(nèi)作為leader管理整個(gè)系統(tǒng);
可見,raft保證一個(gè)term內(nèi)只有一個(gè)server可以被選舉成leader;但有些term內(nèi)可能沒有選出leader,如上圖的term 3,這意味著candidate沒有獲得過半投票,選舉流產(chǎn);一旦出現(xiàn)這種情況,系統(tǒng)立即進(jìn)入新的term時(shí)期,開始新的一輪選舉。
(認(rèn)真理解下這里,并不是leader選舉成功,才進(jìn)入新term;而是舊leader一掛,就進(jìn)入新term;新term一開始必須進(jìn)行選舉,選舉成功則leader登基開始執(zhí)政;選舉不成功則立即進(jìn)入新的term,開始新的一輪;)
每個(gè)term內(nèi)至多有一個(gè)leader;有些term沒有l(wèi)eader,意味著選舉失敗,沒有選出leader;
raft中的每個(gè)server必須保存current term值(當(dāng)前年代、當(dāng)前任期號(hào)),這個(gè)值是當(dāng)前server所認(rèn)為的(best guess,為什么說是guess,是因?yàn)橛袝r(shí)候比如server斷網(wǎng)又恢復(fù)了,它其實(shí)是不知道當(dāng)前term的,只能猜測(cè)現(xiàn)在還處于之前的term中,而這個(gè)猜測(cè)不一定是對(duì)的)當(dāng)前系統(tǒng)所處于的term;這個(gè)term值必須被可靠地存儲(chǔ)在磁盤中,以保證server宕機(jī)重啟之后該值不丟失。
term的作用非常重要,其核心作用是讓raft能夠及時(shí)識(shí)別過期信息,比如某個(gè)認(rèn)為當(dāng)前term是2的server跟另外一個(gè)認(rèn)為當(dāng)前term是3的server進(jìn)行通訊,我們就知道前一個(gè)server的信息是過時(shí)的;我們總是使用最新term的信息;后面的講解中有幾種場(chǎng)景,大家可以看到term用來檢測(cè)并去除過期信息。
(term這種類似概念,在所有分布式一致性算法中都非常重要;其理念類似于搶奪式鎖,用于解決不同term信息的沖突;我們的系統(tǒng)認(rèn)為來自于更大term的信息一定是更準(zhǔn)確的,總是采納來自于最新term的信息;類似于新人勝舊人,后人總是比前人聰明)
slide 7:
?
上圖是整個(gè)raft算法的總結(jié),這里不詳細(xì)說。
簡(jiǎn)要說下圖中展示的幾點(diǎn):
1)各個(gè)角色的行為過程:參見follower、candidate和leader下面的描述;
2)需要持久化到磁盤上的狀態(tài)數(shù)據(jù)(Persistent Satate)、log中每條數(shù)據(jù)的格式;
3)各個(gè)server之間如何交互:raft中所有server之間的通信都是RPC調(diào)用,并且只有兩種類型的RPC調(diào)用:第一種是RequestVote,用于選舉leader;第二種是AppendEntries,用于normal operations中l(wèi)eader向其他機(jī)器復(fù)制log;
現(xiàn)在不詳細(xì)說了,等整個(gè)講完之后,需要反復(fù)回顧這里。
slide 8:
?
現(xiàn)在開始講解算法的第一部分,選舉過程,raft必須保證在任意時(shí)刻,集群中至多只有一個(gè)server充當(dāng)leader。
下面說下啟動(dòng)過程:
1)當(dāng)整個(gè)系統(tǒng)啟動(dòng)的時(shí)候,所有的server都是follower狀態(tài);
2)回憶我們之前所說的,follower是完全被動(dòng)的,它不會(huì)主動(dòng)嘗試聯(lián)系其他server,只能被動(dòng)響應(yīng)來自其他server的信息;但是follower為了保持在自身的follower狀態(tài),它必須要相信集群中存在一個(gè)leader;而follower唯一可能的通信方式就是接收來自leader或candidate的請(qǐng)求;
3)leader為了保持自身的權(quán)威,必須不停地向集群中其他所有server發(fā)送心跳包;而在raft中,心跳信息非常簡(jiǎn)單,就是不帶數(shù)據(jù)的AppendEntries RPC請(qǐng)求;
4)如果過了一段時(shí)間,某個(gè)follow還一直沒收到任何RPC請(qǐng)求,那么它就會(huì)認(rèn)為集群中已經(jīng)沒有可用的leader了,它就會(huì)發(fā)起選舉過程,爭(zhēng)取自己當(dāng)leader;follow的等待時(shí)間,就叫election timeout(選舉超時(shí)),一般是100-500ms;
因此,集群?jiǎn)?dòng)時(shí)候,所有server都是follower,并沒有l(wèi)eader,所有的server都一直等待,直到election timeout,然后所有server都開始競(jìng)選leader;
(感性認(rèn)識(shí)+直覺:leader為了保持自己的權(quán)威地位,必須不停發(fā)心跳;一旦某個(gè)follower在election timeout內(nèi)沒收到心跳,就自認(rèn)為leader已掛,自己翻身開始競(jìng)選leader;類似于皇帝通過發(fā)心跳來壓制子民,一旦某人收不到心跳包,就起身造反,但造反不一定能成功)
slide 9:
?
下面講解選舉過程到底是怎樣的:
1)當(dāng)一個(gè)server開始競(jìng)選leader,第一件事就是增大current term值;所用的這個(gè)current term值必須比系統(tǒng)中所有之前的term值都大;(此處注意,之前說current term值必須是單向遞增,更準(zhǔn)確地說是必須全局單向遞增;即是對(duì)所有server而言的,新term值必須比集群中所有server的已有term值都大)
2)為了進(jìn)行競(jìng)選,這個(gè)follower必須先轉(zhuǎn)變到candidate狀態(tài);在candidate狀態(tài)中,該server只有一個(gè)目標(biāo),就是爭(zhēng)取自己當(dāng)leader;為了當(dāng)leader,它必須要爭(zhēng)取到大多數(shù)投票;
3)先投自己一票;(不想當(dāng)leader的follower不是好的follower;迫切想當(dāng),先投自己一票,這就叫自信!)
4)發(fā)送RequestVote RPC請(qǐng)求到其他所有server,典型場(chǎng)景下是并發(fā)同時(shí)發(fā)送請(qǐng)求的;如果請(qǐng)求發(fā)送出去之后沒有響應(yīng),就會(huì)不斷重試,一直發(fā),直到出現(xiàn)下面三種情況之一:
a.該candidate得到大多數(shù)server的投票:這是我們最希望出現(xiàn)的情況,也是絕大部分情況下會(huì)出現(xiàn)的情況;一旦投票過半,則該candidate立即變成leader狀態(tài),且同時(shí)立即向其他所有follower發(fā)送心跳包;(發(fā)送心跳包是為了宣告天下,保持自己的權(quán)威地位)
b.該candidate收到了來自leader的RPC請(qǐng)求:這說明有其他candidate在同時(shí)跟自己競(jìng)選leader,并且已經(jīng)競(jìng)選成功(注意可以同時(shí)有多個(gè)candidate同時(shí)競(jìng)選);一旦發(fā)現(xiàn)已有l(wèi)eader,則該candidate立即回到follower狀態(tài),接收來自leader的請(qǐng)求,并被動(dòng)響應(yīng)。(一旦競(jìng)選失敗,立即俯首稱臣)
c.過了election timeout的時(shí)間,以上兩種情況都沒發(fā)生:這說明在這段時(shí)間內(nèi)沒有選出任何leader,自己沒當(dāng)選,別人也沒當(dāng)選;在多個(gè)server同時(shí)變成candidate,同時(shí)競(jìng)選的時(shí)候很容易出現(xiàn)這種情況,因?yàn)楹芸赡軐?dǎo)致投票分裂,而沒有一個(gè)candidate獲得過半投票;這個(gè)時(shí)候,處理很簡(jiǎn)單,即回到步驟1)中,增加當(dāng)前current time值,開始新一輪的競(jìng)選;
slide 10:
?
選舉過程中必須保證兩大特性(圖中cont'd是continued的意思,表示接著上一個(gè)slide繼續(xù)):
1)safety:安全性,意思是說在任意一個(gè)給定的term內(nèi),最多只允許一個(gè)server獲勝成為leader;為了保證這一點(diǎn),需要兩個(gè)條件:
a.任意一個(gè)server在一個(gè)term內(nèi)只能投出一票;一旦已經(jīng)投給了一個(gè)candidate,它必須拒絕其他candidate的投票請(qǐng)求;其實(shí)server根本不在意把票投給誰,它只會(huì)把票投給最先到請(qǐng)求到它的candidate;為了保證這一點(diǎn),必須把投票信息持久保存到磁盤上,這樣可以保證即使該server投完票后宕機(jī),稍后又立即重啟了,也不會(huì)在同一個(gè)term內(nèi)給第二個(gè)candidate投票了。
b.只有獲得大多數(shù)投票才能獲勝
結(jié)合a,b:一個(gè)server在同一個(gè)term內(nèi)只能投一票,一個(gè)candidate為了獲勝必須獲得過半投票,顯而易見,在同一個(gè)term內(nèi)不可能有兩個(gè)candidate同時(shí)當(dāng)選;一旦有candidate獲得大多數(shù)投票,其他candidate不可能再獲得過半投票了;不同term內(nèi),當(dāng)然可以有不同的candidate獲勝,但同一個(gè)term內(nèi),只可能有一個(gè)獲勝;
2)liveness:為了保證系統(tǒng)能向前運(yùn)行,我們要確保不能一直都是無leader狀態(tài),必須要能最終選出一個(gè)leader;
問題的關(guān)鍵就是我們要確保不要總是出現(xiàn)splited vote(投票分散),即我們不要讓多個(gè)candidate總是同時(shí)開始競(jìng)選,這很容易使投票分散;同時(shí)開始競(jìng)選,然后投票分散,無法形成大多數(shù)一直,然后等待超時(shí),然后再同時(shí)開始競(jìng)選,這成了一個(gè)惡性循環(huán);
raft的解決辦法很簡(jiǎn)單,即使得election timeout分散開來,不要讓所有server的election timeout都相同,而是在T到2T之間隨機(jī)選擇超時(shí)時(shí)間(T就是election timeout,這個(gè)值通常要比系統(tǒng)中最快的機(jī)器的超時(shí)時(shí)間短);每個(gè)server每次都用隨機(jī)方法計(jì)算出超時(shí)時(shí)間;通過把timeout分散開來,每次取值不一樣,這樣不太可能還會(huì)出現(xiàn)兩個(gè)server同時(shí)超時(shí)然后同時(shí)開始競(jìng)選的情況;總有機(jī)器最先超時(shí),然后有充足時(shí)間在其他server也超時(shí)之前發(fā)起投票、贏得選舉;這種辦法在T遠(yuǎn)大于broadcast time(傳播時(shí)間,指的是server發(fā)起投票、收到投票所花時(shí)間)的情況下效果尤其明顯;
(點(diǎn)評(píng):safety其實(shí)是保證系統(tǒng)一致性運(yùn)行的最低要求,其核心是cannot do something bad,即不能干壞事、不能做錯(cuò)事;liveness其實(shí)是更高要求,意味著不能只是不干壞事,也不能一直不干事,you must do something good,即必須使得整個(gè)系統(tǒng)能良好運(yùn)轉(zhuǎn)下去;因?yàn)槿绻恢碧幱跓oleader狀態(tài),其實(shí)系統(tǒng)是不能對(duì)外提供服務(wù)的;liveness本意就是活性、生存性,在java并發(fā)編程中也有該概念,定義如A concurrent application's ability to execute in a timely manner is known as its?liveness.總結(jié)來說,liveness說的是應(yīng)用程序運(yùn)行及時(shí)性的能力,核心在于要“及時(shí)”執(zhí)行,即在通常認(rèn)為的合理時(shí)間內(nèi)要能執(zhí)行。)
slide 11:
下面開始講第二部分,即leader選舉成功之后的normal operation過程中,leader如何復(fù)制log entries到其他機(jī)器,先說一下log:
1)每個(gè)server都會(huì)保存一份自己私有的log:leader有,各個(gè)followers也都有;這些log保存在各自的機(jī)器上,只供自己操作;
2)log由entry組成,每個(gè)entry都有一個(gè)index,用來標(biāo)記該entry在log中的位置;(就是數(shù)組與元素的關(guān)系)
3)每個(gè)entry內(nèi)包含兩個(gè)東西:
a. command:即可以在state machine上執(zhí)行的具體指令;指令的格式是由客戶端和state machine協(xié)商制定的,consensus module毫不關(guān)心,你可以把它想象成是某個(gè)方法和對(duì)應(yīng)的參數(shù);
b. term number:標(biāo)識(shí)該條entry在哪個(gè)leader的term內(nèi)被創(chuàng)建;之前已經(jīng)說了term是全局單向遞增的,在一個(gè)log內(nèi),term當(dāng)然更是單向遞增(increase monotonically)的;
4)log必須被持久可靠地保存,即log必須保存在磁盤等可靠的存儲(chǔ)介質(zhì)中;另外,server每次收到有l(wèi)og的變更請(qǐng)求,server必須操作完成,并確保log被安全保存之后才再返回請(qǐng)求;
5)如果某個(gè)entry在集群中大多數(shù)server中都有,那么我們認(rèn)為這條entry是committed的(已提交的);這一點(diǎn)在raft系統(tǒng)非常重要;一旦某條entry被committed,這意味著這條entry可以被安全地交給state machine去執(zhí)行;raft保證entry是持久存儲(chǔ)的,并最終被集群中的所有機(jī)器都執(zhí)行;
如圖所示,圖中entry 7是committed,且其之前的1-6也都是committed的,而entry 8不是;
提醒一點(diǎn):過半即是提交,這個(gè)定義并不精確,后面會(huì)稍作修改。(現(xiàn)在你可以暫時(shí)認(rèn)為committed就是指的過半,后面會(huì)看到還會(huì)加一點(diǎn)額外限制條件,用于解決server變化時(shí)候的log一致性問題)
(點(diǎn)評(píng):committed在所有分布式系統(tǒng)中都是一個(gè)重要概念,意味著某條數(shù)據(jù)已經(jīng)得到集體中的大多數(shù)個(gè)體的認(rèn)同,且是持久認(rèn)同;某條命令一旦被committed(得到過半認(rèn)同),就會(huì)被執(zhí)行,并能保證最終系統(tǒng)中的所有機(jī)器都要執(zhí)行,包括最初沒有認(rèn)同的那些機(jī)器;這類似于集體決議,一旦通過,必須執(zhí)行,并最終在所有個(gè)體上都得到執(zhí)行;回想分布式系統(tǒng)的設(shè)計(jì)初衷,即是要保證所有machine最終都執(zhí)行完全一樣的操作,并保持完全一致的狀態(tài);過半即提交,只是實(shí)現(xiàn)最終一致性目的的一種安全而高效的策略而已)
slide 12:
?
normal operation過程非常簡(jiǎn)單:
1)客戶端向leader發(fā)送command請(qǐng)求;
2)leader將command存入自己的log中;
3)leader向所有follower發(fā)送AppendEntries RPC請(qǐng)求;典型場(chǎng)景下leader是并發(fā)請(qǐng)求的,即會(huì)同時(shí)向所有follower發(fā)送相同的請(qǐng)求,并等待接收響應(yīng)結(jié)果;
4)一旦leader接收到足夠多的響應(yīng),即至少一半(加上自己剛好過半,形成大多數(shù)),即認(rèn)為這條entry已經(jīng)被committed,則認(rèn)為可以安全地執(zhí)行這條command了(回憶前面說的,過半即提交,提交即可執(zhí)行):
a. leader一旦認(rèn)為某條entry已committed,則將對(duì)應(yīng)的command傳給它的state machine執(zhí)行,執(zhí)行完成之后返回結(jié)果給client;
b. leader一旦認(rèn)為某條entry已committed,則會(huì)通知其他所有follower;即通過發(fā)送AppendEntries請(qǐng)求通知其他所有follower這條entry已經(jīng)被committed了;最終集群中所有機(jī)器都知道這條entry已經(jīng)被提交了;
c. 一旦followers知道這條entry已經(jīng)被提交了,也會(huì)將對(duì)應(yīng)的command傳遞給自己的state machine執(zhí)行;
(再次總結(jié)下這個(gè)過程:client請(qǐng)求leader-》leader在本地log中寫入entry-》leader向所有followers廣播entry-》leader收到過半響應(yīng),認(rèn)為已提交-》leader執(zhí)行對(duì)應(yīng)命令,返回結(jié)果-》同時(shí)leader向所有followers廣播提交信息-》其他follower獲知已提交則也執(zhí)行對(duì)應(yīng)命令。可見,最終所有server都會(huì)執(zhí)行該請(qǐng)求,但client不會(huì)等到所有server執(zhí)行完,只需等到leader執(zhí)行完即可;但執(zhí)行是有條件的,即log必須復(fù)制到過半server上才能開始執(zhí)行;如果給定時(shí)間內(nèi)仍復(fù)制不到過半server,則本次請(qǐng)求失敗,客戶端必須發(fā)起重試;)
注意幾點(diǎn):
1)如果在這個(gè)過程中,某個(gè)follower掛掉了(crashed)或?qū)eader的RPC請(qǐng)求響應(yīng)特別慢,會(huì)怎么樣?
leader會(huì)一直不斷地重試、重試,直到請(qǐng)求成功;即使follower宕機(jī)了,重啟后leader仍會(huì)接著發(fā)請(qǐng)求,直到請(qǐng)求成功;
需要注意的是,leader并不需要等待所有follower都響應(yīng),只需要收到大多數(shù)server的響應(yīng)以確保log被復(fù)制到大多數(shù)server即可;
因此在通常情況下,性能很好。因?yàn)闉榱送瓿蒫lient請(qǐng)求,并不需要等待所有server,只需要等到集群中過半的server響應(yīng)即可;其實(shí)就是集群中運(yùn)行最快的那部分server;一旦有過半的server響應(yīng),leader即可立即執(zhí)行命令,并返回結(jié)果給client;
這意味著集群中個(gè)別機(jī)器慢或出問題,不會(huì)導(dǎo)致整個(gè)系統(tǒng)變慢,因?yàn)閘eader根本不需要等待那些server;
(注意:有請(qǐng)求進(jìn)來,leader只需等到大多數(shù)機(jī)器有響應(yīng)即可執(zhí)行命令并返回結(jié)果;leader不需要等待所有機(jī)器有響應(yīng)才執(zhí)行命令,但是leader需要不斷發(fā)請(qǐng)求給所有機(jī)器,以確保最終所有機(jī)器都有響應(yīng),并執(zhí)行相同的命令;這是不矛盾的;)
slide 13:
?
raft盡量保證不同server上的log具有高度的一致性;最理想的情況當(dāng)然是任何時(shí)候所有server上的log都完全一樣;當(dāng)然,這是做不到的,因?yàn)榭傆袡C(jī)器可能會(huì)宕機(jī);但是raft還是會(huì)盡可能地保證log的一致性,上圖列出的,就是raft能夠保證的、在任何時(shí)候都成立的一致性承諾:
1)index和term組合起來唯一標(biāo)識(shí)一條entry,即不同server上只要某條entry的index和term相同,則這條entry完全相同(類似于聯(lián)合索引的概念):
a.entry完全相同意思是它們存儲(chǔ)的command一樣;
b.另外,這條entry所有之前的entry也都完全相同;(例如發(fā)現(xiàn)server1和server2在第7個(gè)位置的entry 7相同,則之前的entry1到entry6也一定都完全相同;先記住,后面會(huì)加其他限制來保證這一點(diǎn),且會(huì)有歸納證明;)
因此,結(jié)合a b,得出進(jìn)一步結(jié)論:index和term組合起來唯一標(biāo)識(shí)從開始到index位置的整條log;(這是一個(gè)更強(qiáng)的結(jié)論,意味著不僅當(dāng)前一致,歷史也一致)
2)如果某條entry已被committed,則其之前所有的entries也一定已被committed:
這一點(diǎn)可以從1)推導(dǎo)出來,如發(fā)現(xiàn)entry 7已提交,則意味著entry 7在大多數(shù)server上都存在;則根據(jù)1)可知,entry1到entry6在那些機(jī)器上也一定存在,而那些機(jī)器已構(gòu)成大多數(shù),所以entry1到entry6也一定已被提交;
(點(diǎn)評(píng):這兩點(diǎn)非常重要,貫穿整個(gè)后續(xù)的講解;最核心的就是:index和term唯一標(biāo)識(shí)一條entry;其他結(jié)論都可據(jù)此推導(dǎo)出;一言以蔽之,就是兩句話:a.只要發(fā)現(xiàn)某兩個(gè)log中的某個(gè)entry的index和term都相同,則這兩條log中這條entry包括之前的所有entries都相同 b.只要在某條log中發(fā)現(xiàn)某個(gè)entry已提交,則這條entry之前的所有entries也一定已提交)
slide 14:
?
上一個(gè)slide提到的log之間的一致性,即兩條entry的index和term相同,則其之前的entries也都相同;這個(gè)并不是顯而易見的,需要額外加限制條件來實(shí)現(xiàn),就是通過AppendEntries Consistency Check:
1)當(dāng)leader向follower發(fā)送AppendEntries RPC請(qǐng)求的時(shí)候,除了要發(fā)送新的log entry之外,還要額外帶上兩個(gè)值:即新entry的前一個(gè)entry的index和term;
2)follower收到AppendEntries 請(qǐng)求時(shí),必須先判斷自己的log中是否已有相同的entry(即是否存在entry與請(qǐng)求中的index和term相同),只有匹配上了才會(huì)接受新的entry;如果不匹配,直接拒絕;
如圖中示例1,leader發(fā)送新的entry 5和前面之前entry的index和entry,即(4,2)給follower,follower發(fā)現(xiàn)自己的log中有(4,2)這條entry,所以接收新的entry 5;圖中示例2,leader發(fā)送新的entry 5和緊挨著前一個(gè)entry的index和term,即(4,2)給follower,follower發(fā)現(xiàn)自己沒有(4,2)這個(gè)entry,自己在第4個(gè)位置的entry的term是1,不是2,所以直接拒絕,leader請(qǐng)求失敗;
3)注意2)中的consistency check非常重要,顯而易見,這其實(shí)是個(gè)歸納推理過程:每次接受新entry的時(shí)候,都必須要求前一個(gè)entry必須匹配;以此類推,才能得出前一個(gè)slide里面的結(jié)論:一旦兩條entry相同,則這條entry之前的所有entries也必定相同,也只有如此才能保證log的一致性。
換言之,一旦某個(gè)follower接收了某條entry,則意味著這個(gè)follower的log中從起始位置到這條entry為止的所有entries都跟leader的完全匹配;
至此,normal operation過程講解結(jié)束。
(點(diǎn)評(píng):entry是整個(gè)raft的基石,需要認(rèn)真理解;先想一想,一個(gè)entry中到底有什么東西?如圖所示,entry中有term和command,只有這兩個(gè)信息;而index并不是entry中的信息,而是entry的屬性,即用來標(biāo)記該entry在log中的位置。那為什么之前說如果兩個(gè)entry的index和term相同,則兩個(gè)entry也相同(即command相同)呢?follower的log里的entry都是來自leader,而term相同,說明是來自同一個(gè)leader;而同一個(gè)leader發(fā)送的某個(gè)index位置的entry都是完全相同的;)
?
總結(jié)
以上是生活随笔為你收集整理的分布式一致性算法Raft简介(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker学习笔记(七)docker-
- 下一篇: 分布式一致性算法Raft简介(下)