Raft 共識演演算法

2022-09-30 21:00:20

轉載請註明出處:https://www.cnblogs.com/morningli/p/16745294.html

raft是一種管理複製紀錄檔的演演算法,raft可以分解成三個相對獨立的子問題:

  • 選主(Leader election):原有的leader故障後需要選舉一個新的leader。
  • 複製(Log replication): leader必須接受client傳送的記錄(log entries)然後複製到叢集中其他節點,並強制要求其他節點的紀錄檔和自己保持一致。
  • 安全(Safety):raft安全的關鍵是狀態機安全:如果存在server將一個特定的記錄應用到狀態機中,不存在另外一個server在相同的紀錄檔索引上應用的是不同的命令。

演演算法組成

狀態

  • 所有server上的永續性狀態(在迴應PRC之前更新到穩定儲存(stable storage))
    • currentTerm:已知的最新的任期(term)(初始化為0,單調遞增)
    • votedFor:當前任期內接受投票的candidateId(如果沒有為null)
    • log[]:記錄(log entries);每個記錄包含應用到狀態機的命令以及leader接收該記錄時的任期
  • 所有server上的易變的狀態
    • commitIndex:已知已經提交的最高的記錄索引(初始化為0,單調遞增)
    • lastApplied:已經應用到狀態機的最高的記錄索引(初始化為0,單調遞增)
  • leader上的易變的狀態(選舉後重新初始化)
    • nextIndex[]:對於每個server,需要傳送到這個server的下一條記錄的索引(初始化為leader的最新的記錄索引+1)
    • matchIndex[]:對於每個server,已知已經複製到這個server的最高的記錄索引(初始化為0,單調遞增)

AppendEntries RPC(leader呼叫來複制紀錄檔,也會被用作心跳)

  • 引數
    • term:leader的任期
    • leaderId:follower用來重定向使用者端
    • prevLogIndex:新記錄前一個記錄的索引
    • prevLogTerm:prevLogIndex記錄的任期
    • entries[]:需要儲存的記錄(心跳傳空,為了提高效率可能會傳送多個)
    • leaderCommit:leader的commitIndex
  • 返回
    • term:currentTerm,給leader更新自己的任期
    • success:如果follower包含匹配prevLogIndex和prevLogTerm的記錄返回true
  • 接收者實現
    1. term < currentTerm 返回false
    2. prevLogTerm匹配但是找不到匹配prevLogIndex的記錄返回false
    3. 如果已經存在的記錄與其中一個新記錄(index相同但是term不同)衝突,刪除存在的這條記錄以及後面的所有記錄
    4. 新增不存在的新的記錄到後面
    5. 如果leaderCommit > commitIndex,設定commitIndex = min(leaderCommit, 最新的記錄索引)

RequestVote RPC(被candidate呼叫來收集選票)

  • 引數
    • term:candidate的任期
    • candidateId:請求投票的candidate
    • lastLogIndex:candidate最新的記錄索引
    • lastLogTerm:candidate最新的記錄任期
  • 返回
    • term:currentTerm,給candidate更新自己的任期
    • voteGranted:true表示candidate收到投票
  • 接收者實現
    1. term < currentTerm 返回 false
    2. 如果votedFor是null或者candidateId,並且candidate的紀錄檔至少和自己一樣新,那麼就投票給他

server 需遵守的規則

  • 所有server
    • 如果commitIndex > lastApplied:lastApplied自增,將log[lastApplied]應用到狀態機中
    • 如果RPC請求或者返回包含term T > currentTerm: 設定currentTerm = T,並切換為follower
  • follower
    • 響應candidate和leader的RPC
    • 如果選舉定時器超時沒有收到當前leader的AppendEntries RPC或者沒有向candidate投票:轉換為candidate
  • candidate
    • 在轉變成candidate後就立即開始選舉過程
      • 自增currentTerm
      • 投票給自己
      • 重置選舉定時器
      • 傳送RequestVote RPC給所有其他server
    • 如果接收到大多數server的投票:成為leader
    • 如果接收到新leader發出的AppendEntries RPC:成為follower
    • 如果舉定時器超時:開始新一輪選舉
  • leader
    • 一旦成為領導人:傳送第一個AppendEntries RPC(心跳)給每一個server;空閒時間重複傳送防止選舉定時器超時
    • 如果接收到使用者端的命令:新增記錄到本地紀錄檔後面,在完全應用到狀態機後再響應使用者端
    • 如果最新的記錄索引 >= 某個follower的nextIndex:傳送AppendEntries RPC,包含了從nextIndex開始的記錄
      • 如果成功:更新follower的nextIndex和matchIndex
      • 如果因為紀錄檔不一致導致的失敗:自減nextIndex並重試
    • 如果存在N > commitIndex,大多數的matchIndex[i] ≥ N並且log[N].term == currentTerm:設定commitIndex = N

演演算法不變數

  • Election Safety:每個任期足以多隻有一個leader被選舉出來
  • Leader Append-Only:leader不會覆蓋或者刪除自己的紀錄檔的記錄;他只會在後面新增新的記錄
  • Log Matching:如果兩個紀錄檔包含一個相同索引和任期的記錄,那麼我們認為這個索引的記錄以及之前的記錄的內容完全一致
  • Leader Completeness:如果一個記錄在一個任期內被提交,那麼更高任期的leader的紀錄檔都會包含這個記錄
  • State Machine Safety:如果一個server應用了一個給定索引的記錄到狀態機,不存在其他server在相同的索引位置應用不同的記錄

參考:
https://github.com/maemual/raft-zh_cn