Raft学习笔记
https://ongardie.net/static/raft/userstudy/
保证:
- 一个任期内只有一个leader
三个状态:
leader
follower
candidate
选举:
- 任期加一
- 切换为candidate状态
- 为自己投票
- 向其他所有节点发出投票请求(RequestVote RPCs),重试
- 从大多数节点收到投票
- 成为leader
- 向其他所有节点发送AppendEntries心跳
- 收到合法leader的RPC(=AppendEntries)
- 回退到follower状态
- 没有节点赢得选举(选举超时结束后):【比如有多个节点进入candidate状态,选票分裂了,没有哪个节点收到大多数majority投票】
- 任期加一,开始新一轮选举
- 从大多数节点收到投票
cont’d: continued的缩写,连接上文,续上
性质
安全性:每个任期允许至多一个leader
- 每个节点每个任期内只会发出一个投票(磁盘持久化)
- 同一任期内,不同candidate不会累加选票(可以理解为,要么不存在大多数,要么至多有一个大多数,不会有两个节点都获得大多数选票)
活性:终有一个candidate会胜出
- 随机选择选举超时
[T,2T]
- 一个节点通常在其他节点醒来前超时并赢得选举
- 在
T >> broadcast time
时非常有效
normal operation 常规操作,正常运行
- 客户端向leader发送指令command
- leader将指令追加到日志log
- leader向followers发送AppendEntries RPCs
- 一旦新entry提交了committed:
- leader将指令发给状态机,向客户端返回结果
- leader在随后的AppendEntries RPCs中通知followers提交entry
- followers将指令发给各自的状态机
- 对于宕机或者运行慢的followers:
- leader一直重试RPCs直到成功
- 并不影响leader对客户端的响应,不是必须等待
- 通常情况下,表现理想
- 一次RPC能触及大多数节点
Log Consistency 日志一致性
日志间的高度一致性:
- 如果不同节点的日志entry有相同的index和term,那么:
- 它们存储相同的指令
- 在这之前的日志项entry也都相同
- 如果一个entry是提交的,那么在它之前的entry也是提交的
AppendEntries一致性检查
每个AppendEntries RPC包含前一个entry的index和term
follower包含前一个entry(index和term相同),才接受请求,否则拒绝这个请求
Safety Requirement 安全需求
已提交->保证majority大多数的节点上有最大的index,即使重新选举,那些缺失最大index的节点会选举失败->保证log出现在后续的leader节点上
Election Rules
如果投票节点的日志比candidate的”完整”,则拒绝candidate的选票
主要看term和index 任期和索引
V:投票节点
C:candidate 候选节点
(lastTerm V > lastTerm C) ||
(lastTerm V == lastTerm C) && (lastIndex V > lastIndex C)
Commitment Rules
leader判断一个entry是否已提交,需要考虑
- entry必须存在大多数节点上
- 至少有一个当前leader任期的新entry也存在大多数节点上
第二点不是特别理解..
Client Protocol
- 将指令发送给leader
- 直到指令追加进日志、已提交、并且被leader的状态机执行,leader才会响应客户端
- 如果请求超时(比如leader宕机):
- Client(随机)重发指令给其他节点
- 被定向到一个新leader
- 向新leader重试请求
- 如果leader在执行完指令、响应之前宕机了,为了不执行同一条指令两次,解决方案:
- Client的指令请求里包含unique id
- 节点的log里存储每个请求的id
- leader在接收指令前,检查日志中是否已存在指令中的ID
- 如果存在,忽略新指令,直接返回旧指令对应的响应
- 达到exactly-once semantics 仅执行一次语义,线性一致性