系列文章git
本篇文章想总结下Raft和ZAB在处理一些一致性问题上的作法,详见以前对这2个算法的描述github
上述分别是针对以下算法实现的讨论:算法
一致性算法在实现状态机这种应用时,有哪些常见的问题:微信
1 leader选举学习
1.1 通常的leader选举过程atom
选举的轮次.net
选举出来的leader要包含更多的日志设计
1.2 leader选举的效率日志
会不会出现split vote?以及各自的特色是?server
1.3 加入一个已经完成选举的集群
怎么发现已完成选举的leader?
加入过程是否对leader处理请求的过程形成阻塞?
1.4 leader选举的触发
谁在负责检测须要进入leader选举?
2 上一轮次的leader
3 请求处理流程
3.1 请求处理的通常流程
3.2 日志的连续性问题
3.3 如何保证顺序
3.3.1 正常同步过程的顺序
3.3.2 异常过程的顺序
follower挂掉又链接
leader更换
3.4 请求处理流程的异常
4 分区的处理
下面分别来看看Raft和ZooKeeper怎么来解决的
为何要进行leader选举?
在实现一致性的方案,能够像base-paxos那样不须要leader选举,这种方案达成一件事情的一致性还好,面对多件事情的一致性就比较复杂了,因此经过选举出一个leader来简化实现的复杂性。
更多的有2个要素:
1.1.1 选举投票可能会屡次轮番上演,为了区分,因此须要定义你的投票是属于哪一个轮次的。
他们都须要在某个轮次内达成过半投票来结束选举过程
1.1.2 投票PK的过程,更多的是日志越新越多者获胜
在选举leader的时候,一般都但愿
选举出来的leader至少包含以前所有已提交的日志
天然想到包含的日志越新越大那就越好。
一般都是比较最后一个日志记录,如何来定义最后一个日志记录?
有2种选择,一种就是全部日志中的最后一个日志,另外一种就是全部已提交中的最后一个日志。目前Raft和ZooKeeper都是采用前一种方式。日志的越新越大表示:轮次新的优先,而后才是同一轮次下日志记录大的优先
Raft:term大的优先,而后entry的index大的优先
ZooKeeper:peerEpoch大的优先,而后zxid大的优先
ZooKeeper有2个轮次,一个是选举轮次electionEpoch,另外一个是日志的轮次peerEpoch(即表示这个日志是哪一个轮次产生的)。而Raft则是只有一个轮次,至关于日志轮次和选举轮次共用了。至于ZooKeeper为何要把这2个轮次分开,这个稍后再细究,有兴趣的能够一块儿研究。
可是有一个问题就是:经过上述的日志越新越大的比较方式能达到咱们的上述但愿吗?
特殊状况下是不能的,这个特殊状况详细见上述给出Raft算法赏析的这一部分
这个案例就是这种比较方式会选举出来的leader可能并不包含已经提交的日志,而Raft的作法则是对于日志的提交多加一个限制条件,即不能直接提交以前term的已过半的entry,即把这一部分的日志限制成未提交的日志,从而来实现上述的但愿。
ZooKeeper呢?会不会出现这种状况?又是如何处理的?
ZooKeeper是不会出现这种状况的,由于ZooKeeper在每次leader选举完成以后,都会进行数据之间的同步纠正,因此每个轮次,你们都日志内容都是统一的
而Raft在leader选举完成以后没有这个同步过程,而是靠以后的AppendEntries RPC请求的一致性检查来实现纠正过程,则就会出现上述案例中隔了几个轮次还不统一的现象
Raft中的每一个server在某个term轮次内只能投一次票,哪一个candidate先请求投票谁就可能先得到投票,这样就可能形成split vote,即各个candidate都没有收到过半的投票,Raft经过candidate设置不一样的超时时间,来快速解决这个问题,使得先超时的candidate(在其余人还未超时时)优先请求来得到过半投票
ZooKeeper中的每一个server,在某个electionEpoch轮次内,能够投屡次票,只要遇到更大的票就更新,而后分发新的投票给全部人。这种状况下不存在split vote现象,同时有利于选出含有更新更多的日志的server,可是选举时间理论上相对Raft要花费的多。
1.3.1 怎么发现已完成选举的leader?
一个server启动后(该server原本就属于该集群的成员配置之一,因此这里不是新加机器),如何加入一个已经选举完成的集群
Raft:比较简单,该server启动后,会收到leader的AppendEntries RPC,这时就会从RPC中获取leader信息,识别到leader,即便该leader是一个老的leader,以后新leader仍然会发送AppendEntries RPC,这时就会接收到新的leader了(由于新leader的term比老leader的term大,因此会更新leader)
ZooKeeper:该server启动后,会向全部的server发送投票通知,这时候就会收处处于LOOKING、FOLLOWING状态的server的投票(这种状态下的投票指向的leader),则该server放弃本身的投票,判断上述投票是否过半,过半则能够确认该投票的内容就是新的leader。
1.3.2 加入过程是否阻塞整个请求?
这个其实还要看对日志的设计是不是连续的
若是是连续的,则leader中只须要保存每一个follower上一次的同步位置,这样在同步的时候就会自动将以前欠缺的数据补上,不会阻塞整个请求过程
目前Raft的日志是依靠index来实现连续的
若是不是连续的,则在确认follower和leader当前数据的差别的时候,是须要获取leader当前数据的读锁,禁止这个期间对数据的修改。差别肯定完成以后,释放读锁,容许leader数据被修改,每个修改记录都要被保存起来,最后一一应用到新加入的follower中。
目前ZooKeeper的日志zxid并非严格连续的,容许有空洞
触发通常有以下2个时机
server刚开始启动的时候,触发leader选举
leader选举完成以后,检测到超时触发,谁来检测?
首先看下上一轮次的leader在挂或者失去leader位置以前,会有哪些数据?
一个日志是否被过半复制,是否被提交,这些信息是由leader才能知晓的,那么下一个leader该如何来断定这些日志呢?
下面分别来看看Raft和ZooKeeper的处理策略:
Raft的保守策略更可能是由于Raft在leader选举完成以后,没有同步更新过程来保持和leader一致(在能够对外处理请求以前的这一同步过程)。而ZooKeeper是有该过程的
这其实就和实现有密切的关系了。
Raft的copycat实现为:每一个follower开通一个复制数据的RPC接口,谁均可以链接并调用该接口,因此Raft须要来阻止上一轮次的leader的调用。每一轮次都会有对应的轮次号,用来进行区分,Raft的轮次号就是term,一旦旧leader对follower发送请求,follower会发现当前请求term小于本身的term,则直接忽略掉该请求,天然就解决了旧leader的干扰问题
ZooKeeper:一旦server进入leader选举状态则该follower会关闭与leader之间的链接,因此旧leader就没法发送复制数据的请求到新的follower了,也就没法形成干扰了
这个过程对比Raft和ZooKeeper基本上是一致的,大体过程都是过半复制
先来看下Raft:
再来看看ZooKeeper:
在须要保证顺序性的前提下,在利用一致性算法实现状态机的时候,究竟是实现连续性日志好呢仍是实现非连续性日志好呢?
还有在复制和提交的时候:
其余有待后续补充
具体顺序是什么?
这个就是先到达leader的请求,先被应用到状态机。这就须要看正常运行过程、异常出现过程都是怎么来保证顺序的
3.3.1 正常同步过程的顺序
3.3.2 异常过程的顺序保证
如follower挂掉又重启的过程:
Raft:重启以后,因为leader的AppendEntries RPC调用,识别到leader,leader仍然会按照leader的log进行顺序复制,也不用关心在复制期间新的添加的日志,在下一次同步中自动会同步
ZooKeeper:重启以后,须要和当前leader数据之间进行差别的肯定,同时期间又有新的请求到来,因此须要暂时获取leader数据的读锁,禁止此期间的数据更改,先将差别的数据先放入队列,差别肯定完毕以后,还须要将leader中已提交的议案和未提交的议案也所有放入队列,即ZooKeeper的以下2个集合数据
ConcurrentMap<Long, Proposal> outstandingProposals
Leader拥有的属性,每当提出一个议案,都会将该议案存放至outstandingProposals,一旦议案被过半认同了,就要提交该议案,则从outstandingProposals中删除该议案
ConcurrentLinkedQueue<Proposal> toBeApplied
Leader拥有的属性,每当准备提交一个议案,就会将该议案存放至该列表中,一旦议案应用到ZooKeeper的内存树中了,而后就能够将该议案从toBeApplied中删除
而后再释放读锁,容许leader进行处理写数据的请求,该请求天然就添加在了上述队列的后面,从而保证了队列中的数据是有序的,从而保证发给follower的数据是有序的,follower也是一个个进行确认的,因此对于leader的回复也是有序的
若是是leader挂了以后,从新选举出leader,会不会有乱序的问题?
一旦leader发给follower的数据出现超时等异常
目前ZooKeeper和Raft都是过半便可,因此对于分区是容忍的。如5台机器,分区发生后分红2部分,一部分3台,另外一部分2台,这2部分之间没法相互通讯
其中,含有3台的那部分,仍然能够凑成一个过半,仍然能够对外提供服务,可是它不容许有server再挂了,一旦再挂一台则就所有不可用了。
含有2台的那部分,则没法提供服务,即只要链接的是这2台机器,都没法执行相关请求。
因此ZooKeeper和Raft在一旦分区发生的状况下是是牺牲了高可用来保证一致性,即CAP理论中的CP。可是在没有分区发生的状况下既能保证高可用又能保证一致性,因此更想说的是所谓的CAP两者取其一,并非说该系统一直保持CA或者CP或者AP,而是一个会变化的过程。在没有分区出现的状况下,既能够保证C又能够保证A,在分区出现的状况下,那就须要从C和A中选择同样。ZooKeeper和Raft则都是选择了C。
欢迎关注微信公众号:乒乓狂魔