MS.Microservice
MS.Microservice copied to clipboard
RAFT 一致性算法的理解
参考资料: https://raft.github.io/ https://github.com/maemual/raft-zh_cn
raft核心实现 https://cwiki.apache.org/confluence/display/KAFKA/KIP-595%3A+A+Raft+Protocol+for+the+Metadata+Quorum#KIP595:ARaftProtocolfortheMetadataQuorum-LeaderProgressTimeout
分布式共识算法——Raft
Raft 节点总共有三个状态:
- Leader 主节点
- Follower 从节点
- Candidate 候选节点
Leader 和 Follower 节点之间是有心跳机制的。当 Follower 节点检测到 Leader 节点下线(规定时间内无响应)之后就会进入选举阶段。
选举阶段
通过心跳检测到 Leader 失联,其它 Follower 会随机等待一段时间,然后自举(term 任期数 + 1)开始向其它节点发送投票请求。在投票阶段,各节点会把当前 term 值传递给其它节点,其它 Follower 节点接收到投票请求,会比较自己的 term 和同步进度(即 log index ,后问会讲到),如果都比自己大或相同,则同意请求,否则拒绝该请求。Follower 节点一旦同意就不会再请求其它节点的投票请求了。再其中一个 Follower 节点获得大多数节点的投票之后,会晋升为 Leader 节点。此时 Leader 会立即写入一个 no-op 日志,并进入数据同步阶段。
为什么成为 Leader 之后立马要提交 no-op 日志操作?
因为提交一个空日志操作,可以保持 Raft 协议中的日志一致性,因为其它 Follower 节点收到该日志项后并成功响应,这就代表其它 Follower 节点已经把上一任主节点的日志全部同步完毕(都是最新的日志索引)。
具体来说就有三个方面的原因:
- 日志复制:通过执行一个 no-op 日志操作,并发送给其它节点。其它节点收到日志并复制到自己的日志中,这样所有节点的日志内容就保持一致了。
- 提交确认:当一条日志被 Leader 复制到大多数节点后,Leader 可以将该日志项标记为已提交。执行一个 no-op 日志操作可以确保 Leader 将已提交的信息传播给其他节点。这样,所有节点就会知道该日志项已经被提交,从而在状态机中执行相应的操作。
为什么Raft不能“直接”提交之前任期的日志? | Baixiangcpp's Blog (ilovecpp.com) https://blog.csdn.net/zxpoiu/article/details/115464818 这篇文章讲得更详细
写日志的方式是 WAL
在选举阶段是阻塞的。
数据同步
在数据同步期间,如果 Follower 节点接收到写请求,则将请求转发给 Leader 节点。如果 Leader 接收到写请求,会进行排队直到数据同步结束。
每个节点都有同步进度值,也就是提交日志索引(commit log index),而 Leader 每进行一次写请求时,就会产生一个日志记录。并进行二阶段提交,并将当前 term 值和当前提交日志索引同步给 Follower 节点。Follower 节点接收到请求会比较[^compare]自己 term 和日志索引,只要任意有不一样就进行同步。即 Leader 会根据找到 Follower 节点的索引值在自己的日志倒序找到对应的索引位置,再进行覆盖。
Leader 节点接收到来自大多数 Follower 节点成功响应之后就会正式提交日志索引。在下一次心跳时,Leader 就会把当前最新的 日志索引传递给 Follower。
在数据同步期间,节点是否无法正常提供数据服务。因为这个时候正在等待 Follower 节点的同步响应
[^compare]: 其实节点比较有两个方面的比较:1. 比较当前 term 和当前日志索引、多条操作日志和 term。2. 比较上任 term 和上任日志索引