raft优化

处理只读请求

对于只读请求它不会改变状态机的状态,因此可以直接执行而无需写入日志,这能够大大减少持久化导致的性能开销。但是这样可能导致leader返回过时的结果。比如,只读请求被发送到了一个处于分区中的leader,它的响应很有可能是过时的。

为了解决该问题,在收到只读请求后leader需要执行如下步骤:

  1. leader在自己的任期内必须至少提交过一个当前Term的日志,这可以通过leader当选时立马提交一个no-op请求来实现。这是为了保证leader的commit index至少和其他节点的一样大。
  2. 此时leader将commit index 存储在本地变量readIndex中。
  3. leader需要向其他节点发送心跳确保没有新leader产生,在收到majority的肯定答复后,leader可以确定readIndex是目前集群中最大的commit index
  4. leader等自己的应用状态机成功执行了readIndex(包含)之前的所有日志。
  5. 一切顺利之后,leader可以直接根据自己的状态响应只读请求。

step 1~4是为了确保自己是集群中唯一的leader,并保证自己的状态是最新的,这样leader就可以直接响应只读请求。

再优化:

  • leader还可以只用一轮心跳来完成step1~4,从而批量处理在心跳间隔内累计的只读请求。
  • 集群中的follower也可以执行只读请求,为了保证正确性在执行只读请求之前,follower需要先leader发送请求询问leader的readIndex,此时leader需要完成上述的step1~3,然后follower完成step4,5。

leadership transfer

在Raft中允许leader主动让出自己的领导权,可以应用到如下的两种情形(下述leader为将要转让领导权的节点):

  1. leader必须要让出领导权。比如,leader将要被移除集群。leader可以提交将领导权转让给其它节点,避免在自己被移除后集群因缺乏leader而不可用。
  2. 集群中存在更适合做leader的节点。比如,存在节点它到客户端的延迟更小,那么leader可以将领导权转让给该节点以减少系统的延迟。

leadership transfer的步骤:

  1. leader暂停接受客户端的请求。
  2. leader向目标节点发送日志复制请求,直到目标节点的日志与leader日志一样新。
  3. leader向目标节点发送TimeoutNow请求,目标节点立即增加自己的Term变为candidate并发起一轮选举。

step 2保证目标节点拥有所有已提交的日志,因此它能够在选举中当选。

为了保证Raft的安全性,将要成为新leader的目标节点必须持有所有已提交的日志,

preVote

preVote避免节点无意义地

处于分区中的节点由于收不到leader的心跳,会增加自己的Term并发起选举;如果其RequestVote RPC被发送到集群中,可能会使当前的leader下台,但又由于自己的日志不够新不能顺利当选;这整个过程降低了集群的可用性,为了避免节点发起无意义的选举,Raft可以引入一个preVote阶段。

follower的选举计时器超时后,进入preCandidate状态,不改变自己的votedForTerm字段;向其他节点发送RequestPreVote RPC询问接收者是否会投发送者一票(不会真正的投票),只有收到majority的赞同响应,该节点才会真正的增加Term进入candidate状态,发起选举;如果preVote失败,或者该节点收到 leader 的消息会重新成为 follower,避免了干扰集群。

check quorm

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EeSwMa9h-1631033181316)(assets/image-20210904233648940.png)]

由于网络分区的存在导致集群中存在两个leader(不是同一个Term),如果N1网络的leader接收到客户端的请求也没法提交,因此会延迟客户端请求的执行。

为解决上述请求Raft引入check quorm 机制:leader 每隔 election timeout 检查其他节点的活跃情况,如果在当前分区中少于majority活跃,则自动让出领导权成为follower。

因此发送到N1网络中的请求最多会被延迟一个 election timeout ,这和leader变更导致的延迟时间是相同的。

leader lease

preVote可以避免日志落后的节点扰乱集群,但是没法阻止日志足够新的节点发起选举。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NK9ENPOm-1631033181325)(assets/image-20210904235055084.png)]

一种情况是,在如图所示的网络中,三个节点的日志相同;S1为leader,S1与S3互通,S3与S2互通;由于S2收不到S1的心跳,它发起preVote并受到S3的赞同,于是就会增加自己的Term发起真正的选举,并会导致S1下台,但就目前来看S3没有必要向S2投票。

还有一种情况,如果在成员变更中将一个节点从集群中移除,那么它将收不到leader的心跳,也会发起选举扰乱集群。

为了解决上述问题Raft可以引入leader lease机制:follower 如果在不超过 (最小)election timeout 时间内接收到过 leader 的消息,就不会改变自己的 term 也不会给其他节点投票。因为要发起选举,说明leader至少失联(最小)election timeout以上。

leader lease 需要开启 check quorum:

  • check quorum 是从 leader 角度判断自己是否合法;
  • leader lease 是从 follower 角度判断 leader 是否合法。

若只有 leader lease 而没有 check quorum,会有下面这种情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0NsZ4toW-1631033181328)(assets/image-20210904234657391.png)]

  • 5 个节点的集群,S1 为 leader,S2-S5 为 follower;
  • 发生了网络分区 S1-S2 互通,S2-S4 互通,S5 不通;

如果开启了 leader lease,S2 会忽略其他节点的投票消息,不会选举出新的 leader;要是没有check quorm,S1不会下台那么集群中就无法提交请求,产生活锁。

leader lease是用来避免被移除的节点干扰集群的,是必须的。

处理低Term的消息

默认情况下Raft会忽略Term低于自己的消息,但是这在使用 leader lease 或 preVote 时会带来问题:

  • 开启check quorm & leader lease时,partitioned 节点在网络恢复后一直发送投票请求,但是其他节点 inLease 会忽略它的消息,同时这个节点忽略 leader 的消息(leader的Term较低),导致这个节点不能 stable。
  • 开启preVote时,如果一个节点在preVote阶段获得了majority的赞同增加了自己的Term,但是在正式发送RequestVote RPC前出现网络分区,此时其它节点接受了新的日志,此节点的日志变得落后;如果该节点又进入preVote阶段,那么此次的Pre-Vote 不会成功,且该节点会忽略 leader 的消息,导致这个节点不能 stable。
  • 还有种情况比较 tricky,发生在变更配置时,preVote 从 false 变为 true,若 此时Term 高的节点 log 落后可能会造成集群死锁,不会选举出 leader。 比如有 3 个节点,Term 越高的节点 log 越旧,导致没有一个节点能够 preVote 成功。(见 issue #8501pr #8525)。

所以Raft必须响应上述情况在Term较低的消息:使的接收方能够增加自己的Term。

客户端session

在线性化中,每个操作都在其调用和响应之间的某个时间点立即执行,恰好一次

基于Raft的应用服务要想向外提供线性一致性的接口,就必须避免重复执行同一条命令。应用状态机为每个客户端维护一个session,session中纪录了当前客户端最新执行命令的序列号以及对应的执行结果。如果应用状态机接收到的命令,其序列号已经被执行过了,就立即返回响应不再重复执行命令。

通过扩展该方法,可以允许一个客户端同时发送大量请求。客户端的session不再是只记录最后的序列号及其响应,而是记录一组序列号及其响应;客户端发送的每个请求都携带首个目前尚未收到响应的请求的序列号,应用状态机会删除所有已收到响应的请求的缓存,这类似于TCP协议的滑动窗口,累计确认机制。

服务端就session到期时间达成共识

由于内存有限session到期就要被删除,服务端必须就session的到期时间达成共识,否则应用状态机的状态可能会不一致。例如,一个客户端C的session在S1中已到期,那么S1可能会重复执行C的请求;而C的session在S2中尚未到期,S2不会重复执行C的请求;因此S1、S2的状态会发生分歧。

为了解决该问题,所有的应用状态机必须对具体session的到期时间达成共识,论文中提出了两个方案:

  1. 所有的状态机使用相同的LRU算法,定期清理最久未使用的session。要求淘汰算法必须是确定性的,因为所有的应用状态机执行的请求顺序是相同的,因此应用状态机可以就session的淘汰序列达成共识。
  2. 所有的状态机根据提交的日志,达成时间上的共识,根据时间来淘汰session。客户端会在请求中写入时间戳,状态机在应用请求时根据时间戳就session的持续时间达成共识,同时也对session的到期时间达成共识(在收到客户端最后一个请求之后,一定时间内客户端处于非活动状态就可以删除其session,因此活动的客户端需要定期地发送keep-alive请求)。

Batching 和 pipelining

Raft 天然支持批处理,比如leader会一次性将所有未备份的日志发送到followers,然后followers可以一次性将收到的新日志写入磁盘中。

为了支持流水线,leader 乐观地对待每个 follower 的nextIndex;它在发送前一个条目后立即更新下一个要发送的索引,而不是等待前一个条目的确认。如果 RPC 超时,leader必须将其nextIndex递减回其原始值以重试。如果 AppendEntries 一致性检查失败,leader可能会减少nextIndex以重试发送之前的条目,或者它可能等待之前的条目被确认然后再试一次。(Raft日志复制时的一致性检查,保证了流水线机制的正确性。)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值