小白对Raft的理解

小白对Raft的理解


1. Raft简介

1.1 Raft的前世今生

Raft算法它实际上是一种共识算法,脱胎于Paxos算法。因为Paxos算法不好理解,而且实现起来非常复杂,所以Raft算法就此诞生。

1.2 Raft应用场景

Raft在工程领域的应用特别多,比如Redis的哨兵选举机制、InfluxDB的leader选举、Dragonboat、腾讯云的消息队列CMQ等等。经常被用作CP原则的一致性算法。

2.Raft基础

2.1 Raft中的名词概念

在一个Raft集群中,它有n个服务器构成,其中n最好为奇数。拿我们常见的5服务器集群来说,挂掉的服务器数目只要小于3,整个集群就可以正常工作。

集群中的每个服务器肯定会有下面3种角色中的一个:

  • Leader 它只要负责处理客户端的请求,然后向Follower同步请求日志,只要同步了过半的Follower机器之后就可以Commit了(PS:类似于2PC)
  • Follower 接受并且持久化leader的日志,在Leader节点通知可以commit后,再去commit日志。
  • Candidate 这种状态是在选举的时候出现的状态,正常情况下只会出现前面的那两种角色。当Follower没有收到或者超时收到Leader的消息之后,它就会变成这种状态,然后参加Leader选举。

对于Raft而言,它将时间分为一个个的term,每次term的开始就是Leader的选举。如果成功选举Leader之后,这个term会一直增加,直到Leader挂掉。如果选举失败的话,这个term就会结束,然后开启下一次的term。每一台服务器都会存一个term,这是一个单调递增的数值。在服务器通信的时候,它首先会去检查term,如果这台机器的term比其他机器的term小的话就会去更新为比较大的term

2.2 Raft选举

Raft论文将一致性的问题分为了三个小的子问题:

  • Leader election 选举领导人
  • Log replication 领导人选举成功后,将从客户端接收到日志中,并且Follower节点必须和Leader节点的日志偏移量保持一致
  • safety 安全策略

2.2.1 Leader election

Raft和其他算法一样,都是通过心跳机制来查看机器的情况。Leader会向Follower定时的发送心跳包,如果中间一个Follower在中间并没有收到心跳包,它就会主观的认为Leader已经下线了,然后再开始一次新的选举来选取新的Leader。Follower会先自增自己的term,接着把自己的角色转换为Candidate,然后先给自己投票,再去向集群中的其他机器发送请求投票 RPC。**收到这个请求投票RPC的其他机器 首先会比较term,如果候选人的term< 自身的term的时候就会拒绝 ,然后再去看自己是否已经为其他的候选者发送投票了,如果有的话就拒绝,最后再去看候选人的日志是否没有自己的日志新,如果是的话就拒绝。**等这些全部比较完之后,再去投票,如果当中有一条不符合就不会投票。

这里投票有一个非常重要的原则就是first-come-first-served,谁先到就先考虑谁。所以说,先发送投票的服务器有比较大的可能性赢得选举。

选举之后,这个候选人会有下面的三种状态中的一种:

  • 自己赢得了选举
  • 另外一台服务器赢得了选举
  • 都没有选举成功
赢得了选举

当他赢得了选举之后,就会向其他的服务器发送心跳包,来建立自己的领导地位。

另外一台服务器赢得了选举
if(term>currentTerm){
    //将自己改为follower
}else{
    //拒绝这次的RPC,然后自己的状态不变
}
都没有选举成功

可能是一个候选人没有赢得选举也没输掉选举或者是没有候选人得到大多数的票就会产生这种状况。遇到这种情况的时候自身的term就会自增,然后开始新一轮的选举,一直到选举出Leader为止。但是这种情况很少出现

2.2.2 Log replication

所有的日志都是存在一个log[]数组中,当有leader选举成功后,就会开始接受客户端的请求,每一条请求都会化作新的一条日志,写在log[]数组中,与此同时也会向其他的服务器发送增加日志RPC,让他们复制这条日志。当这条日志被半数以上的服务器成功复制后,就会反馈给Leader节点。然后leader节点再去commit,这就是一套2PC的流程。如果Follower崩了或者因为网络原因导致丢包的话,Leader就会无限的重试发送增加日志RPC,直到所有的Follower存储了日志。

Follower的日志目录比Leader少怎么办?Follower的日志目录有一些Leader中没有的日志怎么办?

在Raft算法中,如果出现了冲突日志的话,会以Leader的为主,而Follower中的冲突日志就会被覆盖。当Leader给Follower发送增加日志RPC的时候,发现Follower的日志和Leader的日志不太一样,这样增加日志RPC就会返回失败,然后再将nextIndex–。直到nextIndex到达Leader和Follower一致的地方,这样的话就会让冲突的日志覆盖,也会补充Follower缺少的日志 。

这里的nextIndex是Leader给所有的Follower维护的一个偏移量,它代表着Leader将要给Follower发送下一条日志的索引。当被选举出Leader的时候,nextIndex就会被默认初始化为它自己的最新的日志条目+1

2.2.3 safety

选举限制

Raft在投票的时候,就杜绝了没有获得全部日志条目的候选者上任为Leader的可能,因为候选人想要成为Leader就代表着,它必须与集群中超过一半的机器进行通信。这就意味着每一条已提交的日志条目至少在一个服务器中存储。如果候选人的日志和大部分机器的日志条目一样新就代表着自己已经提交了全部的日志。如果没有他们新,就会被拒绝候选人的投票请求。

日志安全措施

只要一个日志条目被存在了在多数的服务器上,领导人就知道当前任期就可以提交该条目了。如果领导人在提交之前就崩溃了,之后的领导人会试着继续完成对日志的复制。然而,领导人并不能断定存储在大多数服务器上的日志条目一定在之前的任期中被提交了,就会出现一条存储在了大多数服务器上的日志条目仍然被新上任的领导人覆盖了。

为了解决上面的问题,Raft算法有一个日志匹配原则,当现任的日志条目被提交时,之前所有的日志条目都会被提交。

日志匹配原则:如果两个日志在相同的索引位置上的日志条目的任期号相同,那么我们就认为这个日志从头到这个索引位置之间的条目完全相同

2.3 集群的伸缩

我们的集群总是不可能一直不变的,有时候可能会增加、减少或者更改一些机器的时候,就得要对整个集群重新修改,我们总不能手动的去修改吧,毕竟不论我们时关闭集群还是重启集群都会让集群处于一段时间的不可用状态,而且万一操作失误了怎么办?为了避免我们人工的操作失误,Raft算法也有了自动配置的功能。

为了防止出现,切换配置的时候被选举出了两个Leader,这里是分为两个阶段进行的。它首先会进入到一个过渡状态,这里的过渡状态其实就是指新配置和老配置混合在一块,然后共同选择1个leader。第二阶段就是全部切换为新的配置中。这样也是为了在切换配置的时候,我们的集群还是可以继续向外提供服务。

更新流程

Leader首先创建一个新的日志条目Cold,new配置条目并且将它提交到 Cold,new(使用旧配置的大部分服务器和使用新配置的大部分服务器)。然后创建它创建 Cnew配置条目并且将它提交到使用新配置的大部分机器上。这样就不存在 Cold和 Cnew能够分别同时做出决定的时刻。当一个领导人接收到一个改变配置 Cold 为 Cnew 的请求,它会为了共同一致以前面描述的日志条目和副本的形式将配置存储起来,一旦一个服务器将新的配置日志条目增加到它的日志中,它就会用这个配置来做出未来所有的决定。这意味着领导人要使用 Cold,new 的规则来决定日志条目 Cold,new 什么时候需要被提交。如果领导人崩溃了,被选出来的新领导人可能是使用 Cold 配置也可能是 Cold,new 配置,这取决于赢得选举的候选人是否已经接收到了 Cold,new 配置。在任何情况下, Cnew 配置在这一时期都不会单方面的做出决定。

一旦 Cold,new 被提交,那么无论是 Cold 还是 Cnew,在没有经过他人批准的情况下都不可能做出决定,并且领导人完全特性)保证了只有拥有 Cold,new 日志条目的服务器才有可能被选举为领导人。这个时候,领导人创建一条关于 Cnew 配置的日志条目并复制给集群就是安全的了。另外,每个服务器在收到新的配置的时候就会立即生效。当新的配置在 Cnew 的规则下被提交,旧的配置就变得无关紧要,同时不使用新的配置的服务器就可以被关闭了。Cold 和 Cnew 没有任何机会同时做出单方面的决定;这就保证了安全性。

可能出现的问题

如果新的服务器没有任何日志怎么办?

刚开始加入的新的服务器并没有任何投票的权力,只有等到新服务器将旧服务器的日志同步完成之后,才能拥有投票权

集群的领导人可能不是新配置的一员

Cold的Leader在等Cnew提交的时候就会退化为Follower。

移除不在 Cnew 中的服务器可能会扰乱集群

服务器确认当前领导人存在时,服务器会忽略 RequestVote RPC。特别的,当服务器在当前最小选举超时时间内收到一个 RequestVote RPC,它不会更新当前的任期号或者投出选票。这不会影响正常的选举,每个服务器在开始一次选举之前,至少等待一个最小选举超时时间。然而,这有利于避免被移除的服务器扰乱:如果领导人能够发送心跳给集群,那么它就不会被更大的任期号废除。

2.4 日志压缩

2.4.1 快照

这是最简单的一种压缩方式,把所有状态全部写到快照中,

2.4.2 增量压缩

选择日志清理或者LSM树,不论是哪种操作,都是对一小部分数据进行操作的。因为这样可以分散压力,如果对全部数据进行操作的话,就会阻塞,甚至让服务器宕机。先选择一个有大量垃圾的区域.这里的辣鸡指的是已经被删除或者对象被覆盖的区域,然后重写那个区域活着的对象,接着对整个区域进行释放。

3. Raft的优缺点

3.1 Raft的优点

  • 比Paxos算法更容易理解,⽽且更容易工程化实现。

  • Raft与Paxos一样高效。

  • 适⽤用于permissioned systems(私有链),只能容纳故障节点(CFT),不⽀持作恶节点。最⼤的容错故障节点 是(N-1)/2,其中 N 为集群中总的节点数量。 强化了leader的地位,整个协议可以分割成两个部分:

    • Leader在时。由Leader向Follower同步⽇志,
    • Leader失效了,选一个新Leader。
  • 强调合法leader的唯一性协议,它们直接从leader的⻆度描述协议的流程,也从leader的角度出发论证正确性。但是实际上它们使用了和Paxos完全⼀样的原理来保证协议的安全性。

3.2 Raft的缺点

顺序投票,因此高并发场景下性能差。

PS:作者水平不高,如果中间有什么问题,烦请及时指出,在此感激不尽。

参考

Raft 一致性算法论文译文

Raft 的优缺点

对raft的理解

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读