1、集羣成員變化可能帶來的問題算法
集羣成員變化是一個常見操做,主要是增長、刪除節點,主要的場景有升級、服務器老化等,固然若是咱們對服務的SLA沒太大要求,直接關閉集羣是最簡單的辦法。但若是要保證系統的可用性而動態地添加、刪除節點而且保證不會腦裂等問題則須要一個安全的算法,因此Raft算法把這一部分也歸入其中。安全
直接將集羣成員配置從舊配置切到新配置會有腦裂問題,舉個例子:服務器
上圖中集羣原來有3個節點:Server一、Server二、Server3,如今要增長2個節點:Server四、Server5,咱們來設想下具體的操做步驟:spa
一、Server4和Server5是新節點,因此這2臺機器啓動的時候認爲集羣中有5個節點;日誌
二、而後修改Server3的配置,改爲5節點;blog
這時發生選舉,即上圖中紅色箭頭所指的位置,這時候每一個節點看到的集羣成員以下,爲了方便描述,統一將Server字樣去掉,即只保留一、二、三、四、5,1表示Server1:rem
1:一、二、3同步
2:一、二、3it
3:一、二、三、四、5class
4:一、二、三、四、5
5:一、二、三、四、5
這時1經過2的投票能夠被選舉爲Leader,由於1和2認爲集羣只有一、二、3總共3個節點;
3經過3,4,5這3個節點的投票被選舉爲Leader;
這樣集羣同時存在1和3兩個Leader了。
2、變動方案
官方給的方案是二階段變動:
集羣先從舊成員配置Cold切換到一個過渡成員配置,稱爲共同一致(joint consensus),共同一致是舊成員配置Cold和新成員配置Cnew的組合Cold U Cnew,一旦共同一致Cold U Cnew被提交,系統再切換到新成員配置Cnew。
具體過程以下:
Leader收到從Cold切成Cnew的成員變動請求,Leader分兩步操做:
一、提交配置Cold U Cnew
Leader在本地生成一個新的日誌,這個日誌的類型是成員配置,其內容是Cold∪Cnew,表明當前時刻新舊成員配置共存,寫入本地日誌,但並不提交;
Leader同時將該日誌複製至Cold∪Cnew中的全部副本,在此以後新的日誌同步須要保證獲得Cold和Cnew兩個多數派的確認;
Follower收到Cold∪Cnew的日誌後更新本地日誌,而且立刻就以該配置做爲本身的成員配置;
若是Cold和Cnew中的兩個多數派確認了Cold U Cnew這條日誌,Leader就提交這條日誌;
二、提交Cnew
接下來Leader生成一條新的日誌條目,類型也是成員變動,其內容是新成員配置Cnew,一樣將該日誌條目先寫入本地日誌,同時複製到Follower上;
Follower收到新成員配置Cnew後,將其寫入日誌,而且立刻就以該配置做爲本身的成員配置,而且若是發現本身不在Cnew這個成員配置中會自動退出。
能夠看到,Raft算法將成員配置的變化也做爲一條日誌,須要通過一輪Raft過程像應用操做同樣只要大多數節點確認了就確定不會出出腦裂了。注意Follower收到配置後立刻就變動,而不須要等Leader下次發送commitIndex的時候才提交,這點是和正常應用提交不同的地方。
這裏不去作詳細的證實,官方有詳細說明,咱們能夠看下幾個異常,仍是以上面的例子,當前集羣有3個節點:一、二、3,如今要增長2個節點:四、5,假設當前Leader爲1,具體過程以下:
1)節點1收到成員變動的請求,生成一條日誌類型爲成員變動,內容爲:一、二、三、四、5,節點1將這條日誌先保存到本地,但不立刻更改爲員配置;
2)節點1將上述日誌同步給2,3,4,5四個節點;
3)二、三、四、5節點收到配置後作2件事:追加日誌,立刻變動本身的成員配置爲:一、二、三、四、5;
4)節點1只要收到2個以上節點回復立刻將本身成員配置爲:一、二、三、四、5.
咱們看有哪些異常:
若是有1-2過程當中失敗了,則整個過程就算失敗,則須要管理員從新發起成員變動操做;
若是第3步只有1個節點即不是集羣多數節點收到變動,這個時候節點1掛了,若是收到日誌的先發起選舉則有可能推動這條日誌,不然就不成功,即有可能丟失;
若是在第4步節點1掛了的話,新集羣確定是有這個配置的,由於根據日誌最新原則,新選舉出來的Leader確定包含上面成員變動的日誌。
3、其它問題
一、新加的成員入無日誌
一開始的時候新的服務器可能沒有任何日誌條目,若是它們在這個狀態下加入到集羣中,那麼它們須要一段時間來更新追趕,在這個階段它們還不能提交新的日誌條目,這個時候節點沒有投票權,有的文章說叫Catch-Up。
二、移除不在 Cnew 中的服務器可能會擾亂集羣
主要是移除的狀況,設想一個集羣有5個節點:1,2,3,4,當前Leader是2,如今要把1移除掉,假如2已經將新的成員配置:2,3,4已經同 步給3和4了,若是配置不發給1,由於2認爲集羣中1不存在了,因此不會向1發心跳,而1沒收到2的心跳,會增長本身的Term發起選舉,其它成員收到後會退化成Follower,不過不會給1投票,由於1的日誌不是最新的,不過這會影響集羣的可用性。
針對這個問題,官方給的是PreVote,即在發起投票的RequestVote RPC請求以前再發一個PreVote請求,只有經過這個才正式發起RequestVote,這樣能夠大大提高系統的可用性。其實還有1個更簡單的辦法,直接將1關機或者將服務程序Kill掉。