基於Raft 的分佈式一致性協議是構建不少分佈式服務的基礎,某種程度上它充當了心臟的角色,爲此有必要對Raft 的一些難點進行深刻理解。安全
一個常見的誤解就是複製到多數副本的就能夠視做commited, 其實還不夠。缺乏必須已經執行了對應的操做這個步驟。我的理解在實際 Raft使用過程當中,就是存在某個Raft log 在append到多個副本的瞬間宕機了,因爲尚未執行on_apply() ,其實尚未向上層用戶ACK 這條日誌已經成功。
所以這條日誌能夠truncate, 也能夠後續隨着後續的log commited 以後本身也被commited。網絡
這個比較大規則以下:
某個follower 收到 vote 請求以後,會計算出收到請求的時間和上次收到主心跳的時間間隔,而後把這個間隔和一個約定的較小時間進行比較。若是前者還小(此時說明還有其餘節點長時間沒有收到主的心跳,發起了vote),拒絕此次vote 請求,不然投同意票。app
考慮三個互聯互通的IDC A、B、C,B是主副本,若是因爲某種緣由B、C網絡不通了,若是沒有上面限制,B發起帶有最高term信息的vote請求,A會一樣vote請求,這樣主就從B變成了C。同理,過了之後,主又極可能從C再變成B。如此 反覆循環,整個複製組上沒有辦法提供持續穩定的主。分佈式
這個規則以下:
某個follower 收到 vote 請求以後,在current term和請求中的term 相等的狀況下:須要使用當前節點的LastLogIndex 、LastLogTerm和請求中的LastLogIndex、LastLogTerm進行比較,若是前者大,則拒絕請求;若是後者大,則贊成請求。ide
考慮下,爲啥要這樣?仍是考慮上面三個互聯互通的IDC A、B、C,B是主副本,若是因爲某種緣由B、C網絡不通了,C是劃分節點。當網絡劃分以後,C的current Term是最高的,它極可能發起vote,這樣把B Stepdown了,A和B的currentTerm 提高了;但是由於C的LogIndex很低,它發到A和B的Vote 請求,在同A、B的Last LogIndex 比較時發現本身過小,而無法拿到A、B的信任票,所以Vote失敗。隨後A或B再發起Vote , 就是用最高的Term(C的Term+1 )、最新的LastLogIndex(A 或B的LastLogIndex),必定會選主成功,而且擁有最新的數據。日誌
Raft採用的成員變動單策略相對簡單,每次只增刪一個節點,這樣就不會出現兩個多數集合,不會形成決議衝突的狀況。按照以下規則進行處理:
按照上面的規則,能夠實現安全的動態節點增刪,由於節點動態調整跟Leader選舉是兩個並行的過程,節點須要一些寬鬆的檢查來保證選主和AppendEntries的多數集合:code
爲了不同時有兩個節點變動正在進行,在有未committed的change正在進行的時候,不容許進行節點變動。節點變動有一個問題,對一個只有兩個節點的Cluster,發起RemovePeer。這個時候一個節點掛掉,另一個節點沒有收到RemovePeer請求,這樣系統將中止工做。所以強烈建議集羣節點數>=3個。pdo