轉載請註明出處: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
- 接收者實現
- term < currentTerm 返回false
- prevLogTerm匹配但是找不到匹配prevLogIndex的記錄返回false
- 如果已經存在的記錄與其中一個新記錄(index相同但是term不同)衝突,刪除存在的這條記錄以及後面的所有記錄
- 新增不存在的新的記錄到後面
- 如果leaderCommit > commitIndex,設定commitIndex = min(leaderCommit, 最新的記錄索引)
RequestVote RPC(被candidate呼叫來收集選票)
- 引數
- term:candidate的任期
- candidateId:請求投票的candidate
- lastLogIndex:candidate最新的記錄索引
- lastLogTerm:candidate最新的記錄任期
- 返回
- term:currentTerm,給candidate更新自己的任期
- voteGranted:true表示candidate收到投票
- 接收者實現
- term < currentTerm 返回 false
- 如果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