raft协议是分布式一致性协议的一种实现方案,那么什么是分布式一致性,这就还得需要了解其他的网络知识了
为了向用户提供服务,就需要有对应的服务器提供,在早些年的时候,很多网络应用服务器节点都只有一个,这个时候并不会有分布式一致性的问题,因为一个客户端把数据写到服务器要么写成功,要么写失败,没有其他的情况,但是会引起一个单节点故障的问题,如果当前服务器节点挂掉以后,那么整个网址就无法提供服务了,因此随着时代的发展,必然需要多服务器节点来共同提供服务,在这种情况下,就会有新的问题引入,如下
CAP的意思如下
C: 数据一致性,
A: 服务可用性
P: 分区容错性
首先,在一个分布式系统中,P是必须要保证的,P的意思是说在我们的集群中,由于网络存在很多不可控的因素,节点中有可能会失去联系,但是这种情况下我们不能直接让整个集群都无法服务,所以这个P是必须要考虑,那么剩下的就是说要实现A或者C的问题
A是指服务可用性,它带来的好处就是说只要集群中还有一个节点存活,那么客户端就可以使用,这样一来系统的可用性就会提高,但是他同时带来的问题也是很明显的,如果客户端刚写完数据到某个节点1,然后就返回成功给客户端了,此时节点1刚要同步给其他节点的时候挂掉了,那么节点1的数据就会丢失了,而客户端明明收到了成功的响应,这就会带来数据不一致的问题
P是指数据一致性,它带来的好处跟A其实就是反过来的,一般CP架构下都会有个leader节点,客户端所有的写操作都需要经过leader节点来操作,此时写数据是这样的,客户端写一个5的数据给CP架构的集群里,此时leader数据先写成功,然后leader会同步给其他的节点,当整个集群有半数以上的节点数据写成功以后,才会响应给客户端写入成功,这样一来,数据一致性就有了保证,很明显CP架构的缺点就是A的优点,由于需要同步数据给其他节点,那么性能肯定会比较低了
base原则是对CAP理论进行补充的,因为一般情况下同时实现CAP是不存在的,但是可以尽可能的兼容三者,而BASE就是为了解决这个问题的
BA: 基本可用
S: 软状态
E: 最终一致性
也就是说当集群某个节点挂掉以后,集群仍然要对外可用,如果是因为分区引起的网络不通,那么此时集群节点中的数据不是一致的,这种情况称为软状态,但是当节点重启后或者网络分区恢复后,数据落后的节点要自己主动拉取最新的数据进行同步,同步后的状态便称为最终一致性了
为了提高性能,其实都是基于半数机制,而不是真正意义的等待所有节点同步后才返回的,因为那样就太耗性能了
最后,对一个分布式系统来说,只能同时保证两个特性,比如我们Java开发常见的一些中间件架构,redis是AP架构,zookeeper是CP架构,nacos可以支持AP或者CP架构,而raft协议就是实现CP架构的一种协议,下面就来说说这个协议
对于raft协议,国外有一个比较好的网站,也可以直接参考这个网站,个人对于raft协议的理解也是基于这个网站:Raft分布式一致性协议
raft协议分为两个部分,集群选举和数据复制
在使用raft协议的分布式系统中节点有三种状态
Candidate :竞选者,当集群中没有leader节点时
Follower:追随者,追随leader的节点
Leader :集群中的领导者
集群选择的大概过程,具体可以直接看网站,更清晰
1. 刚开始集群所有的节点状态都为Follower状态
2. 所有的节点会随机休眠一定时间,在150ms-300ms之间
3. 当某个节点苏醒后它会先投自己一篇,然后把投票信息发给其他的节点,其他节点假设还没有苏醒,那么就会响应这个投票,解设当前节点收到的票数超过集群节点的半数,那么当前节点就会变为Leader 状态,如果收不到,那么又会重新随机休眠
4. 当某个节点变为Leader状态时,Leader会发送请求给其他的Follower节点,Follower节点收到消息后会响应给Leader,此时Leader就会与所有的Follower保持心跳,Follower节点会先从Leader同步一次数据,后面就一直保持心跳了
这个过程就是集群选举的流程,假设此时的Leader节点挂掉以后,那么就会进入重新选举的流程
1. 心跳得不到响应以后,也就是Leader挂掉了,那么其他的Follower节点就会发起重新的选举,选举的流程和上面都是一样的
2. 只是多了一步选取完Leader以后Follower会从Leader拉取一次同步数据
当集群选举出Leader以后,就进入到数据复制过程了,raft使用的是二阶段提交的方式,主要的过程如下
1. 客户端向集群中发送一个数据5到Leader中,如果接收到请求的不是Leader节点,那么会被自动转到Leader节点上
2.Leader节点收到数据5以后写入自己本地日志中,此时数据处于未提交状态
3. Leader写完本地日志以后便会通过下次心跳机制把数据传送给其他的节点
4. 其他节点收到数据以后也在自己的节点上写入数据,此时也还是未提交的数据,接着就会响应给Leader节点,Leader节点接收到半数的节点通知后就会把之前的数据设置为已提交,然后反馈给客户端写入成功
5. Leader接着再发送消息给其他节点,其他节点收到以后也会把自己本地日志写为提交状态
至此,数据复制过程就结束了,值得一提的是raft是支持网络分区的
解设集群有5个节点A,B,C,D,E,如果在某个时刻下,A和B形成了一个小集群,C,D,E形成了一个小集群,两个小集群中节点互不相通,此时每个小集群都会有自己的Leader,也就是客户端可以往两个Leader写数据,但是raft协议的设计中,如果客户端往A,B集群写数据,由于raft协议存在着半数机制,客户端虽然可以写未提交的数据给A数据的Leader节点,但是整个小集群只有两个节点,达不到半数的要求,所以数据会一直停留在未提交的状态,而另一个集群是可以写数据
当网络分区结束后,两个小集群就会合并为大集群,是数据也会同步一致,至此,便解决了这个问题
使用CountDownLatch来保证半数节点同步成功机制
心跳是从Leader发送给其他节点的
心跳发送的数据只包括key信息,并且对发送的信息进行了压缩处理
从节点收到数据以后会再次整理需要更新的key,然后再根据key去主节点提供的接口拉取数据进行同步,这种设计就比较巧妙了,数据同步的过程不至于给Leader带来过大的压力,当然了也会这样做就会有数据稍微延时的情况存在,从节点还进行了优化,如果数据不到50就不去Leader节点拉取数据,这一步是为了减少网络的传输,也是一个可以参考的设计点
raft协议的基本过程并不复杂,而且参考这个网站基本也能看懂
当然了这个协议可能还存在一些其他的细节问题,这里就暂时不讨论了,有空的可以再查查其他的资料