学学raft协议

raft 协议

Nacos 默认支持 AP,但同时又支持 CP ,底层使用 raft(jraft)实现 leader 的选举,所以我们需要学学 raft

什么是raft

简单说Raft是一套共识算法,是 paxos 的变种,解决分布式系统中多个副本的一致性问题,通过多副本来进行容错,以此在提高系统可用性。

说到底raft的诞生就是为了更简单的解决分布式系统中每个系统的一致性问题

那么raft怎么解决一致性问题呢?

raft将一致性问题分为

  1. leader选举
  2. 日志复制
  3. 安全措施

leader 选举:就是在整个分布式系统中选出一个 主节点 对外提供服务

日志复制:就是让从节点和主节点的状态保持一致

安全措施:就是在选举的过程中(或者整个系统存在一些问题时),保证系统依旧可用

接下来,我们需要对上面三个部分进行分析

1. leader选举

Raft共识算法可视化

raft协议在进行leader选举的过程中,会将节点置于三个状态:

  1. follow 从节点,给其他节点提供选票
  2. candidate候选者,由 follow 变化而来,如果一个 follow 在一定时间内没有收到 leader 的心跳包时,就会将自己的状态修改为 candidate,并开启新一届的任期term,启动新一轮的选举流程
  3. leader主节点,如果candidate选举成功,则转化为 leader ,并开始对外提供服务

在分布式系统刚开始前所有节点都是 follow 从节点

image.png

然后follow发现没有收到 leader 的心跳包,将自己的状态修改为 candidate 候选者状态

image.png

接着候选者向其他从节点发送选票

image.png

从节点将会回复选票给候选者

image.png

右下角的节点就变成 Leader

image.png

需要注意,Term = 0 任期包含选举的过程哦!

所有对系统的修改都将跟 Leader 挂钩

leader选举过程结束

节点间的数据复制

image.png

发送给 leader 节点的数据将会被记录在节点的日志中,此时由于数据没有同步到其他节点,所以 leader 节点的值没有变成 5

image.png

leader 向其他从节点发送数据:

image.png

从节点将值记录到自己的日志中,然后发送ackleader 节点, 告诉他我已经把值保存到日志中了

image.png

此时 leader 节点的值将会被修改为 5 ,并且标记日志中的这条记录为已提交

image.png

接着 leader 将发送消息给其他从节点,表示我的值已经修改了,你们也可以将日志的值提交

image.png

此时整个系统达到共识,日志复制过程结束

raft 中有两个超时设置控制着leader选举

第一个是 选举超时,选举超时是 follow 变成 candidate 的计算时间,只要超时,则该从节点就会变成 candidate

而选举超时时间不可能被设置成相同的时间,这样会导致多个节点的选举超时时间都被耗尽,全变成candidate候选者

一般超时时间介于 150ms ~ 300ms 之间随机选取

image.png

当一个 follow 节点变成了 candidate 时,term将会被自增1,接着为自己投一票,并且发送投票消息给其他节点

image.png

如果接受投票消息的节点在这个 term (此时从节点的term也自增为 1)中还没有投票,那么它会投给 candidate 候选者

image.png

从节点发送ack完毕之后,将自己的 选举超时 时间重置,重新开始倒计时

candidate 收到从节点的 ack 之后,立即变成 leader

leader 第一时间发送 append entries 消息给其他从节点

image.png

这些消息将以心跳超时指定的间隔时间发送

接着从节点响应 append entries 消息回 leader

image.png

这中间将数据进行了一次同步,演示里面没说明

最后选举的过程结束,直到从节点 选举超时 时间超时,从节点变成新一任的 candidate

现在新的问题发生了, leader 节点从集群中消失了,此时从节点选举时间超时,变成 candidate ,term++,接着发出 vote 投票消息给其他从节点,此时集群中只有两个节点

一个是 candidate 另一个是 follow

follow ack 消息给 candidateterm++ 并重置 选举超时时间,

candidate 接收到 ack 变成 leader 之后发送 append entries message 给 follow节点

这中间 新 leader 还是会发送 发送心跳包 给 Node B(已经挂掉的节点)

image.png

分裂投票情况(脑裂问题)

两个follow节点在某个时间同时变成 candidate 导致集群出现脑裂问题,raft怎么解决这个问题?

image.png

image.png

此时每个 candidate 获得的选票都是 2 , 相等了,且没有更多有票的 follow

raft 给出的方案就是选票数量重置为 0 ,再选一次

因为每个 选举超时 的时间大概率不相等,所以此时选举大概率会选举出一个 leader

image.png

节点状态变化图:

img

2. 日志复制

一旦选出了一个leader之后,我们需要将系统所有更改的数据复制给所有节点

image.png

通过使用相同心跳包的 append entries 消息完成日志复制

来看看完整过程:

首先通过客户端发送一个数据给 leader, leader收到数据保存到日志

image.png

这个数据在下次心跳包时会发送给 follows

image.png

一样的步骤,follows 返回ackleaderleader立即将日志的数据提交到leader

image.png

此时 leader 响应一个消息给 client(也就是图中的绿色部分)

image.png

leader发送已经保存的消息给其他follows,告诉他们可以将日志的数据提交了

现在客户端再次发送一个消息,add 2 将 leader 的数据从5 增加到 7,这里就不详解了,具体步骤相同

image.png

甚至 raft 可以在面对网络分区时保持一致

现在添加一个网络分区,去分离所有节点

image.png

image.png

分离之后上面的三个节点需要重新选择leader

image.png

此时上面三个节点的 term 任期会被下面的任期大一级,因为上面进行了选举

image-20220704171001090

下面的 client 发送 set 3Node B leaderNode B 发现无法发送给绝大多数的节点

Node B 还记录着ACDE节点的信息

发现只有 Node A 做出了反应,绝大数节点还没反应,所以无法将值写入到节点中

但不影响 Node C leader 修改其 follows 的能力

image.png

图中client发送了 set 8Node C leaderNode C 只记录了 Node DNode E 所以可以将日志中的值提交,并拷贝给 Node DNode E

现在我们修复网络分区问题

此时可以看到 Node C leaderterm 高于下面的 Node B leader,所以 Node B leaderLeader状态被撤销

image.png

Node BNode A 都将回滚未提交的消息,并配合 Node C leader 的日志

也就是说原先的 set 3 消息没了?

对,没了。。。客户端不会收到 set 成功的消息了

如果想深入研究 raft 可以去看他的 paper: In Search of an Understandable Consensus Algorithm (raft.github.io)

至此 raft 入门结束

猜你喜欢

转载自juejin.im/post/7116445621082390565