consul微服务架构运用了raft
算法
节点状态
假设有三个节点
A
B
C
节点处于任意状态
追随者
领导者
候选人
所有节点启动时都是处于一个follower
状态,在一段时间内如果没有收到来自领导者
的心跳包,都会从follower
转换成candidate
状态。
哪个节点做leader是大家投票选举出来的,每个leader工作一段时间,然后选出新的leader继续负责。这根民主社会的选举很像,每一届新的履职期称之为一届任期,在raft协议中,也是这样的。
选举步骤如下
- 增加节点本地的任期,切到候选人状态
- 投自己一票
- 并行给其他节点发送请求
- 等候其他节点的回复
赢得选举之后,新的领导者
会立刻给所有节点广播,避免其余节点又发起新的选举。
在任一任期内,单个节点只能投一票
先到先得
比如A,B发起选举,A的消息先到C,C给A投了一票,当B的消息到达时,C就不能再投票给B了,A.B都只会给自己投一票,所以A胜出后,B,C收到心跳消息
官方推荐是用3,5个节点开启集群服务器的,选举超时随机设置在150ms-300ms
split vote
因为有一半的数量挂了,就会停止服务,比如开4个,挂掉2个,还不如开5个挂掉2个还能正常运行,其次是因为防止平票出现
ABCD,CD同时吃到了AB的分别一票,会出现一个平票情况。又会延长了系统不可用的时间(没有leader是不能处理客户端写请求的)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eKuBh3fD-1637741216597)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211124160225753.png)]
因此引入了共识算法,节点的个数都是奇数
共识算法的实现一般是基于复制状态机
相同的初识状态 + 相同的输入 = 相同的结束状态
不同节点要以相同且确定性的函数来处理输入,而不要引入一下不确定的值,比如本地时间等。
在raft中leader将客户端请求封装成一个个log ,将log复制到所有的follower节点,然后大家按照相同的输入,保持相同的状态。
客户端完整请求
- leader追加日志条目
- leader并发添加PRC日志条目
- leader等待多数人回应
- leader回复client
- leader广播follower应用日志,相当于确定所有节点都可以更改日志达成共识了
raft算法为了保证高可用,并不是强一致性,而是最终一致性,leader会不断尝试给follower发log entries
,催着所有节点的log entries都相同才算完
。
leader只需要日志被复制到大多数节点即可向客户端返回,一旦向客户端返回成功,那么必须保证log不能回滚了。leader广播follwer是节点将日志应用到状态机,真正影响到节点状态了。
safety属性
在任何系统模型下,都需要满足safety属性,即在任何情况下,系统都不能出现不可逆的错误,也不能向客户端返回错误的内容。比如,raft保证被复制到大多数节点的日志不会被回滚,那么就是safety属性。而raft最终会让所有节点状态一致,这属于liveness属性。
没事儿
有事也是好事
选举安全性
即一个任期内最多一个leader被选出来,任何时刻只能有一个leader,两个leader会导致数据覆盖丢失。
一个节点某一任期内最多只能投一票
只有获得大多数投票才能成为leader保证了这个安全性
状态机安全
某个leader选举成功后不会直接提交前任leader时期的日志,而是通过提交当前任期的日志的时候顺手把之前的日志也提交了。还会在任期开始的时候发立即尝试复制、提交一条空的log。
如果是追随者还有未提交的日志条目,并且领导重新选举了,会匹配新领导者的日志。
不同节点,某个位置上日志相同,那么这个位置之前的所有日志一定是相同的
raft将公式问题分解成两个问题
先是领导人选举
志条目,并且领导重新选举了,会匹配新领导者的日志。
不同节点,某个位置上日志相同,那么这个位置之前的所有日志一定是相同的
raft将公式问题分解成两个问题
先是领导人选举
后是leader负责复制、提交log和接受客户端请求