天天看點

分布式一緻性算法Raft簡介(轉載)

https://www.jianshu.com/p/ee7646c0f4cf

https://www.jianshu.com/p/10bdc956a305

最近看了Ongaro在2014年的博士論文《CONSENSUS: BRIDGING THEORY AND PRACTICE》的部分章節,對raft有了初步的了解。其中論文中提到用于教學的user study,個人感覺非常不錯,言簡意赅,特此分享出來。本文基本與原講解一緻,又加上了筆者的一點了解。

資源來源于Ongaro和Ousterhout在youtube上的分享(http://youtu.be/YbZ3zDzDnrw),共有31個slide,因篇幅字數限制分為上下:

上:分布式一緻性算法Raft簡介(上)

下:分布式一緻性算法Raft簡介(下)

slide 1:

John Ousterhout,對分布式一緻性算法Raft進行簡單介紹。

slide 2:

Raft的總體目标是将log完全一樣地複制到叢集中的所有機器上,用來建立所謂的Replicated State Machine(多副本狀态機,就是具有多個copy的應用程式)。

設想你有一個程式或應用,你想讓它非常可靠,其中一個辦法就是,在多個機器上同時運作這同一個程式,并且保證完全一樣地運作,這就是replicated state machine的思想。state machine是個抽象概念,就是指一個程式或應用,能接收請求,并能輸出結果(可以了解為有輸入能輸出的程式黑盒子)。(注意state machine指的是一個具體的應用程式,不是通常了解的機器;server才是指的通常的機器或執行個體。)

引入log這個概念,有助于使這些state machines執行完全一樣的指令。下面解釋下運作過程:如果一個用戶端,想執行一個command(指令,指令,指的是具體的某個操作請求),那麼它可以請求其中一台state machine,這台machine就把這個指令,如command X,記錄到自己的本地日志log中,另外還要把command X傳遞給其他所有machines;其他machine也在各自的log中記錄下這條指令;一旦這條command X被safely replicated(安全地複制)到所有machine的所有log中,那麼這個command X就可以被傳遞給各個machine開始執行,一旦有machine執行完成指令X,就會把結果傳回給用戶端。

(大家注意這個過程:收到請求command->記錄本地log->将log複制到其他machine->一旦認為log被安全地複制完成->才允許各個machine開始執行command->一旦有machine執行完成command->即把command的執行結果傳回給用戶端。這也就意味着不是所有請求command都能被最終執行,隻有那些“安全地複制”完成的,才允許執行。特别注意了解什麼叫安全地複制完成,直覺了解就是隻要複制完成,就是最終狀态,不允許再反悔再更改;我了解是兩點,一是複制必須持久化,如寫磁盤;二是必須複制到叢集中大多數machine,得到大多數的承認;)

是以可以看到,隻要所有log和machine都完全一樣,且所有不同server(機器)上的所有state machine都按照完全一樣的順序執行log中的相同指令,那麼所有machine必定輸出完全一樣的結果。(大家注意下這個推理過程:1 所有機器上都部署相同的machine應用;2 因為log是複制過去的,安全地複制保證了所有機器上的所有machine的log都完全一樣;3 所有機器上的所有machine都按照log中的相同順序執行指令;4 log是相同的,log中相同位置的指令也是相同的;5 所有機器上的所有machine都一樣,執行的指令也一樣,輸出的結果也必定完全一樣。其實這裡還有個隐含條件,就是machine應用必須是deterministic的,即确定性的,由輸入必定能推出唯一的輸出,不能帶有随機性模糊性;其實跟我們通常了解的應用是一緻的;)

Consensus Module(一緻性子產品,圖中有标注,是machine裡的一個協調控制子產品)的工作職責是管理這些logs,確定被合理地複制,并決定什麼時候将這些logs中的command送出給state machine執行(其實consensus module就是一個協調子產品,相當于整個系統的大腦,是整個算法的控制核心);之是以叫consensus based算法,是因為我們并不要求所有機器都是一直線上并運作良好。事實上為了保證效率,隻要求大多數機器線上,并能互相通信即可。(consensus在英文中的本意是共識、一緻意見、大多數人的意見)比如,一個叢集中3台機器,可以容忍1台機器故障,隻要有2台線上即可;再比如5台機器的叢集,可以容忍2台機器故障,隻要3台線上即可。這裡簡要說下我們的系統能處理的異常:機器可以crash,可以停止運作,可以暫停運作再過段時間恢複,但要求運作的時候必須正常(意思是你可以罷工,但你在崗的時候幹活兒必須正确);是以我們不能處理Byzantine failures(拜占庭将軍問題,指的是惡意篡改資料這類行為);我們也允許網絡中斷、消息丢失、消息延遲、消息傳遞亂序、網絡分化等。(意思就是我們正常分布式環境中會遇到的網絡問題、消息丢失延遲等問題,raft都能應對;唯一不能處理的就是惡意篡改資料這類,比如受到惡意網絡攻擊、或者你惡意篡改磁盤中的log日志等這類非正常行為)。

slide 3:

實作一緻性算法有兩種可能方式:

1)第一種叫對稱式,或無leader:這種方式中所有server的角色完全一樣,權利也一樣,在任意時刻的行為也基本一樣,所有server都是對等的;用戶端可以請求任意一台server,将command寫入log中,并複制到其他server(其實就是完全平等制、無政府主義);

2)第二種叫非對稱式,或leader-based:在任意時間各個server都是不平等的,某個時刻隻有一個server是leader,管理叢集中的所有操作;其他server都是被統治的,隻能簡單按照leader的旨意幹活;在這類系統中,用戶端隻能與leader通信,也隻有leader能與其他server交流(其實就是專制獨裁統治,隻有一個leader,可以為所欲為,其他所有人都必須聽命于leader,隻有leader能對外交流);

raft采用的就是第二種leader-based的方式,并且把一緻性問題分解為兩個不同問題:一是在有leader的情況下叢集如何正常運作,二是leader挂掉之後如何選舉更換leader。raft采用的這種leader-based方式的優勢是使得正常運作過程非常簡單,因為你不需要擔心多個leader同時指揮導緻的沖突,記住隻有一個leader,leader可以為所欲為;raft算法的所有複雜性其實都來自于leader變更,這是因為舊leader忽然挂掉,可能導緻整個系統處于不一緻的狀态,新leader上任後必須收拾殘局。(後面大家會有切身體會,raft正常的運作過程非常簡單,但是leader變更過程非常複雜)

通常來講,有leader的系統比無leader的系統效率更高,原因很簡單,你不需要擔心多個server之間的沖突,你隻需要額外處理下leader變更流程;(獨裁不一定是壞事,效率更高!)

slide 4:

raft講解提綱,共6個部分:

1、leader選舉:如何從多個server中挑選一台作為leader;當leader挂掉之後,如何感覺到,并挑選出新的leader來替換舊leader;

2、正常運作(最基本的log複制過程):leader從用戶端收到請求後如何将log複制到其他機器;這其實是整個raft算法中最簡單的部分;

3、leader變更過程中的安全性和一緻性:leader變更是raft中最難的,也是最關鍵的;首先會講下safety到底意味着什麼,以及如何保證safety;接着講新leader上任後如何處理log,使得整個系統恢複一緻性狀态;

4、neutralize 舊leader:這是leader變更中的另一個問題,就是舊leader并沒有真的死掉,死灰複燃,重新恢複之後,我們該如何處理;

5、用戶端互動:用戶端如何與整個系統互動?關鍵點是請求過程中server挂掉了client怎麼辦?如何實作linearizable semantics(線性化語義),即每個用戶端指令隻能執行一次(once and exactly once,其實就是防止出現多次執行出問題,類似于幂等性概念);

6、配置變更:如何在叢集中新增、或删除機器?

slide 5:

開始講解6個部分之前,先從整體上說下系統中的server狀态:

在任意時刻,系統中的任意一個server,隻能是以下3種狀态中的一種:

1)leader:同一時刻至多隻能由一個server處于leader狀态,處理所有的用戶端互動、日志複制等;(同一時刻至多一個,意思是要麼一個leader,要麼沒leader)

2)follower:大部分時候叢集中的絕大部分server都是follower的狀态,這些server是完全被動的狀态,即不能主動送出請求,隻能響應來自其他server的請求;(意思就是不能主動問别人,隻能别人問了你回答;don't ask me! I ask you, you answer only!)

3)candidate:這是一個從follower到leader之間的中間狀态;隻是leader選舉過程的一個臨時狀态;

正常情況下的叢集狀态應該是:1個leader,其他所有都是follower。

上圖的下半部分展示了server狀态的變遷過程:

1)follower想成為leader,必須先變成candidate,然後發起選舉投票;如果投票不足,仍回到follower狀态;如果投票過半,變成leader;

2)leader故障恢複後發現已經有新leader了,則自動下台,進入follower狀态;

slide 6:

時間被劃分成一個個的term(這裡的term,還有zookeeper中的epoch,都是同一個意思,指的就是任期、時期、年代;指的是某一個leader的統治時期),每個term都有一個number,這個number必須單向遞增且從未被用過;

每個term時期,分兩部分,一是為這個term選舉leader的過程,二是一旦選舉成功,這個leader在這個term的剩餘時間内作為leader管理整個系統;

可見,raft保證一個term内隻有一個server可以被選舉成leader;但有些term内可能沒有選出leader,如上圖的term 3,這意味着candidate沒有獲得過半投票,選舉流産;一旦出現這種情況,系統立即進入新的term時期,開始新的一輪選舉。

(認真了解下這裡,并不是leader選舉成功,才進入新term;而是舊leader一挂,就進入新term;新term一開始必須進行選舉,選舉成功則leader登基開始執政;選舉不成功則立即進入新的term,開始新的一輪;)

每個term内至多有一個leader;有些term沒有leader,意味着選舉失敗,沒有選出leader;

raft中的每個server必須儲存current term值(目前年代、目前任期号),這個值是目前server所認為的(best guess,為什麼說是guess,是因為有時候比如server斷網又恢複了,它其實是不知道目前term的,隻能猜測現在還處于之前的term中,而這個猜測不一定是對的)目前系統所處于的term;這個term值必須被可靠地存儲在磁盤中,以保證server當機重新開機之後該值不丢失。

term的作用非常重要,其核心作用是讓raft能夠及時識别過期資訊,比如某個認為目前term是2的server跟另外一個認為目前term是3的server進行通訊,我們就知道前一個server的資訊是過時的;我們總是使用最新term的資訊;後面的講解中有幾種場景,大家可以看到term用來檢測并去除過期資訊。

(term這種類似概念,在所有分布式一緻性算法中都非常重要;其理念類似于搶奪式鎖,用于解決不同term資訊的沖突;我們的系統認為來自于更大term的資訊一定是更準确的,總是采納來自于最新term的資訊;類似于新人勝舊人,後人總是比前人聰明)

slide 7:

上圖是整個raft算法的總結,這裡不詳細說。

簡要說下圖中展示的幾點:

1)各個角色的行為過程:參見follower、candidate和leader下面的描述;

2)需要持久化到磁盤上的狀态資料(Persistent Satate)、log中每條資料的格式;

3)各個server之間如何互動:raft中所有server之間的通信都是RPC調用,并且隻有兩種類型的RPC調用:第一種是RequestVote,用于選舉leader;第二種是AppendEntries,用于normal operations中leader向其他機器複制log;

現在不詳細說了,等整個講完之後,需要反複回顧這裡。

slide 8:

現在開始講解算法的第一部分,選舉過程,raft必須保證在任意時刻,叢集中至多隻有一個server充當leader。

下面說下啟動過程:

1)當整個系統啟動的時候,所有的server都是follower狀态;

2)回憶我們之前所說的,follower是完全被動的,它不會主動嘗試聯系其他server,隻能被動響應來自其他server的資訊;但是follower為了保持在自身的follower狀态,它必須要相信叢集中存在一個leader;而follower唯一可能的通信方式就是接收來自leader或candidate的請求;

3)leader為了保持自身的權威,必須不停地向叢集中其他所有server發送心跳包;而在raft中,心跳資訊非常簡單,就是不帶資料的AppendEntries RPC請求;

4)如果過了一段時間,某個follow還一直沒收到任何RPC請求,那麼它就會認為叢集中已經沒有可用的leader了,它就會發起選舉過程,争取自己當leader;follow的等待時間,就叫election timeout(選舉逾時),一般是100-500ms;

是以,叢集啟動時候,所有server都是follower,并沒有leader,所有的server都一直等待,直到election timeout,然後所有server都開始競選leader;

(感性認識+直覺:leader為了保持自己的權威地位,必須不停發心跳;一旦某個follower在election timeout内沒收到心跳,就自認為leader已挂,自己翻身開始競選leader;類似于皇帝通過發心跳來壓制子民,一旦某人收不到心跳包,就起身造反,但造反不一定能成功)

slide 9:

下面講解選舉過程到底是怎樣的:

1)當一個server開始競選leader,第一件事就是增大current term值;所用的這個current term值必須比系統中所有之前的term值都大;(此處注意,之前說current term值必須是單向遞增,更準确地說是必須全局單向遞增;即是對所有server而言的,新term值必須比叢集中所有server的已有term值都大)

2)為了進行競選,這個follower必須先轉變到candidate狀态;在candidate狀态中,該server隻有一個目标,就是争取自己當leader;為了當leader,它必須要争取到大多數投票;

3)先投自己一票;(不想當leader的follower不是好的follower;迫切想當,先投自己一票,這就叫自信!)

4)發送RequestVote RPC請求到其他所有server,典型場景下是并發同時發送請求的;如果請求發送出去之後沒有響應,就會不斷重試,一直發,直到出現下面三種情況之一:

a.該candidate得到大多數server的投票:這是我們最希望出現的情況,也是絕大部分情況下會出現的情況;一旦投票過半,則該candidate立即變成leader狀态,且同時立即向其他所有follower發送心跳包;(發送心跳包是為了宣告天下,保持自己的權威地位)

b.該candidate收到了來自leader的RPC請求:這說明有其他candidate在同時跟自己競選leader,并且已經競選成功(注意可以同時有多個candidate同時競選);一旦發現已有leader,則該candidate立即回到follower狀态,接收來自leader的請求,并被動響應。(一旦競選失敗,立即俯首稱臣)

c.過了election timeout的時間,以上兩種情況都沒發生:這說明在這段時間内沒有選出任何leader,自己沒當選,别人也沒當選;在多個server同時變成candidate,同時競選的時候很容易出現這種情況,因為很可能導緻投票分裂,而沒有一個candidate獲得過半投票;這個時候,處理很簡單,即回到步驟1)中,增加目前current time值,開始新一輪的競選;

slide 10:

選舉過程中必須保證兩大特性(圖中cont'd是continued的意思,表示接着上一個slide繼續):

1)safety:安全性,意思是說在任意一個給定的term内,最多隻允許一個server獲勝成為leader;為了保證這一點,需要兩個條件:

a.任意一個server在一個term内隻能投出一票;一旦已經投給了一個candidate,它必須拒絕其他candidate的投票請求;其實server根本不在意把票投給誰,它隻會把票投給最先到請求到它的candidate;為了保證這一點,必須把投票資訊持久儲存到磁盤上,這樣可以保證即使該server投完票後當機,稍後又立即重新開機了,也不會在同一個term内給第二個candidate投票了。

b.隻有獲得大多數投票才能獲勝

結合a,b:一個server在同一個term内隻能投一票,一個candidate為了獲勝必須獲得過半投票,顯而易見,在同一個term内不可能有兩個candidate同時當選;一旦有candidate獲得大多數投票,其他candidate不可能再獲得過半投票了;不同term内,當然可以有不同的candidate獲勝,但同一個term内,隻可能有一個獲勝;

2)liveness:為了保證系統能向前運作,我們要確定不能一直都是無leader狀态,必須要能最終選出一個leader;

問題的關鍵就是我們要確定不要總是出現splited vote(投票分散),即我們不要讓多個candidate總是同時開始競選,這很容易使投票分散;同時開始競選,然後投票分散,無法形成大多數一直,然後等待逾時,然後再同時開始競選,這成了一個惡性循環;

raft的解決辦法很簡單,即使得election timeout分散開來,不要讓所有server的election timeout都相同,而是在T到2T之間随機選擇逾時時間(T就是election timeout,這個值通常要比系統中最快的機器的逾時時間短);每個server每次都用随機方法計算出逾時時間;通過把timeout分散開來,每次取值不一樣,這樣不太可能還會出現兩個server同時逾時然後同時開始競選的情況;總有機器最先逾時,然後有充足時間在其他server也逾時之前發起投票、赢得選舉;這種辦法在T遠大于broadcast time(傳播時間,指的是server發起投票、收到投票所花時間)的情況下效果尤其明顯;

(點評:safety其實是保證系統一緻性運作的最低要求,其核心是cannot do something bad,即不能幹壞事、不能做錯事;liveness其實是更高要求,意味着不能隻是不幹壞事,也不能一直不幹事,you must do something good,即必須使得整個系統能良好運轉下去;因為如果一直處于無leader狀态,其實系統是不能對外提供服務的;liveness本意就是活性、生存性,在java并發程式設計中也有該概念,定義如A concurrent application's ability to execute in a timely manner is known as its liveness.總結來說,liveness說的是應用程式運作及時性的能力,核心在于要“及時”執行,即在通常認為的合理時間内要能執行。)

slide 11:

下面開始講第二部分,即leader選舉成功之後的normal operation過程中,leader如何複制log entries到其他機器,先說一下log:

1)每個server都會儲存一份自己私有的log:leader有,各個followers也都有;這些log儲存在各自的機器上,隻供自己操作;

2)log由entry組成,每個entry都有一個index,用來标記該entry在log中的位置;(就是數組與元素的關系)

3)每個entry内包含兩個東西:

a. command:即可以在state machine上執行的具體指令;指令的格式是由用戶端和state machine協商制定的,consensus module毫不關心,你可以把它想象成是某個方法和對應的參數;

b. term number:辨別該條entry在哪個leader的term内被建立;之前已經說了term是全局單向遞增的,在一個log内,term當然更是單向遞增(increase monotonically)的;

4)log必須被持久可靠地儲存,即log必須儲存在磁盤等可靠的存儲媒體中;另外,server每次收到有log的變更請求,server必須操作完成,并確定log被安全儲存之後才再傳回請求;

5)如果某個entry在叢集中大多數server中都有,那麼我們認為這條entry是committed的(已送出的);這一點在raft系統非常重要;一旦某條entry被committed,這意味着這條entry可以被安全地交給state machine去執行;raft保證entry是持久存儲的,并最終被叢集中的所有機器都執行;

如圖所示,圖中entry 7是committed,且其之前的1-6也都是committed的,而entry 8不是;

提醒一點:過半即是送出,這個定義并不精确,後面會稍作修改。(現在你可以暫時認為committed就是指的過半,後面會看到還會加一點額外限制條件,用于解決server變化時候的log一緻性問題)

(點評:committed在所有分布式系統中都是一個重要概念,意味着某條資料已經得到集體中的大多數個體的認同,且是持久認同;某條指令一旦被committed(得到過半認同),就會被執行,并能保證最終系統中的所有機器都要執行,包括最初沒有認同的那些機器;這類似于集體決議,一旦通過,必須執行,并最終在所有個體上都得到執行;回想分布式系統的設計初衷,即是要保證所有machine最終都執行完全一樣的操作,并保持完全一緻的狀态;過半即送出,隻是實作最終一緻性目的的一種安全而高效的政策而已)

slide 12:

normal operation過程非常簡單:

1)用戶端向leader發送command請求;

2)leader将command存入自己的log中;

3)leader向所有follower發送AppendEntries RPC請求;典型場景下leader是并發請求的,即會同時向所有follower發送相同的請求,并等待接收響應結果;

4)一旦leader接收到足夠多的響應,即至少一半(加上自己剛好過半,形成大多數),即認為這條entry已經被committed,則認為可以安全地執行這條command了(回憶前面說的,過半即送出,送出即可執行):

a. leader一旦認為某條entry已committed,則将對應的command傳給它的state machine執行,執行完成之後傳回結果給client;

b. leader一旦認為某條entry已committed,則會通知其他所有follower;即通過發送AppendEntries請求通知其他所有follower這條entry已經被committed了;最終叢集中所有機器都知道這條entry已經被送出了;

c. 一旦followers知道這條entry已經被送出了,也會将對應的command傳遞給自己的state machine執行;

(再次總結下這個過程:client請求leader-》leader在本地log中寫入entry-》leader向所有followers廣播entry-》leader收到過半響應,認為已送出-》leader執行對應指令,傳回結果-》同時leader向所有followers廣播送出資訊-》其他follower獲知已送出則也執行對應指令。可見,最終所有server都會執行該請求,但client不會等到所有server執行完,隻需等到leader執行完即可;但執行是有條件的,即log必須複制到過半server上才能開始執行;如果給定時間内仍複制不到過半server,則本次請求失敗,用戶端必須發起重試;)

注意幾點:

1)如果在這個過程中,某個follower挂掉了(crashed)或對leader的RPC請求響應特别慢,會怎麼樣?

leader會一直不斷地重試、重試,直到請求成功;即使follower當機了,重新開機後leader仍會接着發請求,直到請求成功;

需要注意的是,leader并不需要等待所有follower都響應,隻需要收到大多數server的響應以確定log被複制到大多數server即可;

是以在通常情況下,性能很好。因為為了完成client請求,并不需要等待所有server,隻需要等到叢集中過半的server響應即可;其實就是叢集中運作最快的那部分server;一旦有過半的server響應,leader即可立即執行指令,并傳回結果給client;

這意味着叢集中個别機器慢或出問題,不會導緻整個系統變慢,因為leader根本不需要等待那些server;

(注意:有請求進來,leader隻需等到大多數機器有響應即可執行指令并傳回結果;leader不需要等待所有機器有響應才執行指令,但是leader需要不斷發請求給所有機器,以確定最終所有機器都有響應,并執行相同的指令;這是不沖突的;)

slide 13:

raft盡量保證不同server上的log具有高度的一緻性;最理想的情況當然是任何時候所有server上的log都完全一樣;當然,這是做不到的,因為總有機器可能會當機;但是raft還是會盡可能地保證log的一緻性,上圖列出的,就是raft能夠保證的、在任何時候都成立的一緻性承諾:

1)index和term組合起來唯一辨別一條entry,即不同server上隻要某條entry的index和term相同,則這條entry完全相同(類似于聯合索引的概念):

a.entry完全相同意思是它們存儲的command一樣;

b.另外,這條entry所有之前的entry也都完全相同;(例如發現server1和server2在第7個位置的entry 7相同,則之前的entry1到entry6也一定都完全相同;先記住,後面會加其他限制來保證這一點,且會有歸納證明;)

是以,結合a b,得出進一步結論:index和term組合起來唯一辨別從開始到index位置的整條log;(這是一個更強的結論,意味着不僅目前一緻,曆史也一緻)

2)如果某條entry已被committed,則其之前所有的entries也一定已被committed:

這一點可以從1)推導出來,如發現entry 7已送出,則意味着entry 7在大多數server上都存在;則根據1)可知,entry1到entry6在那些機器上也一定存在,而那些機器已構成大多數,是以entry1到entry6也一定已被送出;

(點評:這兩點非常重要,貫穿整個後續的講解;最核心的就是:index和term唯一辨別一條entry;其他結論都可據此推導出;一言以蔽之,就是兩句話:a.隻要發現某兩個log中的某個entry的index和term都相同,則這兩條log中這條entry包括之前的所有entries都相同 b.隻要在某條log中發現某個entry已送出,則這條entry之前的所有entries也一定已送出)

slide 14:

上一個slide提到的log之間的一緻性,即兩條entry的index和term相同,則其之前的entries也都相同;這個并不是顯而易見的,需要額外加限制條件來實作,就是通過AppendEntries Consistency Check:

1)當leader向follower發送AppendEntries RPC請求的時候,除了要發送新的log entry之外,還要額外帶上兩個值:即新entry的前一個entry的index和term;

2)follower收到AppendEntries 請求時,必須先判斷自己的log中是否已有相同的entry(即是否存在entry與請求中的index和term相同),隻有比對上了才會接受新的entry;如果不比對,直接拒絕;

如圖中示例1,leader發送新的entry 5和前面之前entry的index和entry,即(4,2)給follower,follower發現自己的log中有(4,2)這條entry,是以接收新的entry 5;圖中示例2,leader發送新的entry 5和緊挨着前一個entry的index和term,即(4,2)給follower,follower發現自己沒有(4,2)這個entry,自己在第4個位置的entry的term是1,不是2,是以直接拒絕,leader請求失敗;

3)注意2)中的consistency check非常重要,顯而易見,這其實是個歸納推理過程:每次接受新entry的時候,都必須要求前一個entry必須比對;以此類推,才能得出前一個slide裡面的結論:一旦兩條entry相同,則這條entry之前的所有entries也必定相同,也隻有如此才能保證log的一緻性。

換言之,一旦某個follower接收了某條entry,則意味着這個follower的log中從起始位置到這條entry為止的所有entries都跟leader的完全比對;

至此,normal operation過程講解結束。

(點評:entry是整個raft的基石,需要認真了解;先想一想,一個entry中到底有什麼東西?如圖所示,entry中有term和command,隻有這兩個資訊;而index并不是entry中的資訊,而是entry的屬性,即用來标記該entry在log中的位置。那為什麼之前說如果兩個entry的index和term相同,則兩個entry也相同(即command相同)呢?follower的log裡的entry都是來自leader,而term相同,說明是來自同一個leader;而同一個leader發送的某個index位置的entry都是完全相同的;)

繼續閱讀