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

最近看了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都是完全相同的;)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容