Raft 算法—集群配置变更、Log压缩、客户端交互

1. 配置变更

集群的配置会包含在 log entries (使用特殊的 entries)中,并复制到其他节点上。

设集群的旧配置为 C o l d C_{old} Cold,新配置为 C n e w C_{new} Cnew(如, C n e w C_{new} Cnew 中添加或移除了某些节点)。

Raft 在新旧配置之间引入了一个中间状态,称为 joint consensus,其包含了新旧配置,即其配置为 C o l d , n e w C_{old,new} Cold,new,也就是说,此阶段的集群成员为 C o l d C_{old} Cold 中的成员和 C n e w C_{new} Cnew 中的成员的并集。一旦 joint consensus 被提交了,系统就会切换到新配置。 当集群处于 joint consensus 阶段时:

  • log entries 会被复制到所有的节点上,包括 C o l d C_{old} Cold C n e w C_{new} Cnew 中的节点。(例如, C n e w C_{new} Cnew 中将某些节点移出/添加入了集群,此时 C o l d C_{old} Cold C n e w C_{new} Cnew 所包含的节点集合便不相同)
  • C o l d C_{old} Cold C n e w C_{new} Cnew 中的节点都具有成为 leader 的资格。
  • 选举共识、entry 提交共识需要分别获得来自 C o l d C_{old} Cold C n e w C_{new} Cnew 中的大多数节点的同意。

(1)当 leader 接收到来自管理员的配置变更请求时,它会将 C o l d , n e w C_{old,new} Cold,new 存储为一条 log entry,并复制到其他节点上。

(2)一旦服务器将 C o l d , n e w C_{old,new} Cold,new 添加到自己的 log 中后,它便会使用该配置,而不管该配置是否已被提交。

(3)leader 会根据 joint consensus(即, C o l d , n e w C_{old,new} Cold,new)的规则来提交 entry;一旦 C o l d , n e w C_{old,new} Cold,new 被提交,则只有具备 C o l d , n e w C_{old,new} Cold,new entry 的节点才有资格成为 leader。

(4)然后,leader 会创建一条新的 entry,其中只包含 C n e w C_{new} Cnew,并将其复制到其他节点上。

(5)一旦服务器将 C n e w C_{new} Cnew 添加到自己的 log 中后,它便会使用该配置,而不管该配置是否已被提交。

(6)leader 会根据 C n e w C_{new} Cnew 的规则(不需再考虑 C o l d C_{old} Cold)来提交 entry;当 C n e w C_{new} Cnew 被提交之后,不在 C n e w C_{new} Cnew 中的节点便可以关闭了。

2. 配置变更之后的问题

当配置变更之后,可能会存在如下问题。

问题1

新加入的服务器可能初始时便没有存储任何 log entries,这可能会耗费较长的时间来等待新节点追上其他节点的进度,而且此间新节点可能无法提交新的 log entries。

为了避免此种情况,新节点会先以 non-voting 成员的身份加入集群。此时,该节点不具备投票权,也不被认为是大多数;但 leader 仍会向它复制 log entries。一旦该节点追上了其他节点的进度,它便恢复正常的身份。

问题2

leader 可能不是新配置的一部分,即新配置中将 leader 移出了集群。

此时,leader 会继续复制 entries 到其他节点,但不将它自己看作是大多数中的一部分(因为 C n e w C_{new} Cnew 中已经没有该节点了)。在提交 C n e w C_{new} Cnew entry 之后便退回到 follower 的身份。

问题3

被移除的节点(不在 C n e w C_{new} Cnew中,且它们还未被下线)不再接收到来自 leader 的心跳信号,因此它们会以更大的 term 号开始新的选举,并向其他节点发送 RequestVote RPC。

为了避免上述情况,节点会在接收到来自 leader 的心跳信号后的选举超时时间内忽略来自其他节点的 RequestVote RPC。

3. Log 压缩

当 log 越来越长时,它便会占用更多的空间,且花费更多的时间来重放(replay)。

Raft 使用了快照技术来解决 log 过长的问题。即首先将系统的状态写入一个快照,然后删除该时间点之前的所有 log entries。

(1)每个节点会进行独立的快照,当然只涉及那些已被提交的 entries。
(2)为了 AppendEntries RPC 的需要,快照中会包含 last included index(最后被状态机执行的 entry 的 index) 和 last included term(上述 entry 对应的 term 号) 这两个元数据,其对应的 entry 是没有被存入快照的。
(3)考虑到集群配置变更的问题,节点还会将最新的配置存入快照。
(4)在完成快照之后,节点便可以删除 last included index 之前的所有 entries 及之前的快照。
(5)如果其他节点太过于落后了,leader 会通过 InstallSnapshot RPC 将自己的快照发送给其他的节点;如果节点包含的信息确实过于落后,则它会使用 leader 的快照来覆盖自己的 log;否则,节点会删除自己的 log 中包含 leader 发过来的快照中的那部分,但保留快照之后的 log entries。

执行快照通常需要花费较多的时间开销,为了不影响正常的操作,Raft 使用了写时复制技术来进行快照。具体而言,Raft 会通过 fork() 来创建一个子进程(通过写时复制技术共用父进程的地址空间,因此也具有快照所需的数据),然后在子进程中执行快照,父进程继续处理之前的逻辑。

4. 客户端交互

由 leader 负责处理所有的客户端请求。

(1)客户端刚启动时,它会连接到一个随机选择的服务器;如果该服务器不是 leader,则该服务器会拒绝该请求,并将 leader 的信息返回给客户端。

(2)如果 leader 崩溃了,客户端请求会超时,然后它会再次连接到一个随机选择的服务器。

(3)为了使每条命令只被执行一次,Raft 为每条命令都赋予了一个序列号;然后状态机会为每个客户端跟踪一个最新的序列号及相应的响应;如果服务器接收到一条已被执行的命令,则它会立即返回对应的响应,而不会再次执行该命令。

(4)对于读操作而言,为了防止返回陈旧的数据,leader 会在 term 的起始时提交一条 no-op entry(空操作),以查明已被提交的有哪些 entries;此外,为了防止 leader 被新的 leader 取代后仍响应只读操作,leader 会在响应只读操作前向大多数节点发送心跳,以确定它是否还是 leader。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值