一、paxos算法
paxos算法是由兰伯特与1990年提出的一个分布式系统的共识算法。分布式系统的共识算法通俗易懂的来说就是让分布式系统中的所有的服务器就一个问题达成一个一致性的方案。而Zookeeper的基础就是paxos算法。
1、首先paxos算法中有三个不同的角色,分别为提案者、接受者、learner
当客户端发来一个写请求后,提案者收到后会发起一个提案(Proposal),每个提案都有一个自己的全局唯一ID,而且这个ID是自增的不会减少。随后这个提案者会将这个提案发送给每个接受者的队列中,接受者从自己的队列中取出这个提案,将这个提案与自己记录的提案的ID进行比较,如果队列中的这个提案的ID比自己记录的ID大的话接受者就会接收这个提案给提案者发送一个ACK,如果发现队列中的ID不大于自己记录的ID的话,就会否决认为这个提案已经被人提交过了,不会给提案者发送ACK。
2、 只有当提案者收到超过半数的ACK的时候才会认为自己的提案通过了。可以理解为两阶段提交,但是和常规的两阶段提交的不同的地方在于只要有超过半数的机器同意了就可以第二次提交了。这也是Zookeeper为什么快的一个原因,只要保证半数可用即可。然后会将这个提案commit提交到所有的机器中并且生效。learner的等级比接受者低,因为不参与投票的步骤。但是最后客户端的响应是由learner给客户端的。
上述的步骤就保证了在一个分布式系统中,所有的机器可以就客户端的一个请求达成一致性的方案并且给客户端响应。这就是paxos算法的核心。
但是paxos算法有一些问题,比如如果同时有多个提案者发起提案的话会产生一个活锁问题(下面会讲),分布式系统中就无法对一个问题达成一致性的解决方案。为了解决这个问题,zookeeper做了一些改进。
二、paxos中的活锁问题
因为有多个提案者会发起提案,而每个提案的ID是全局唯一并且递增的,所以当有多个提案者同时发起提案的时候,这个ID就会成为一个竞争资源,而会出现一个提案者一直没有获取到这个ID,虽然没有被阻塞,但是会一直重复“尝试-失败-尝试-失败”的流程,这个就叫做活锁。
三、Leader的选举
为了解决上述的活锁问题,兰伯特提出了一个解决方案,就是给Zookeeper集群设置一个唯一的Leader(领导),只能由这个领导发起提案,即使客户端将请求发给了follower的话,follower也会将这个请求转发给leader。这样就解决了竞争全局ID导致的活锁问题。 所以就引出了Zookeeper集群中leader是如何选举出来的这个问题。
leader的选举分为两种情况,
第一种情况就是zookeeper集群刚启动的时候如何选举出来leader。
第二种情况是运行期间leader挂了怎么选举出来leader。 下面针对这两种情况详细说明。
在进行说明leader选举过程之前,先补充几个概念,每个服务器有一下几个核心的标识
SID:服务器的全局ID,对应myid
ZXID:类似于事务ID,没进行一次更新操作的时候ZXID会自增,不会减少
Epoch:leader任期ID,每个leader任期的时候都会有一个自己的ID,类似于王朝的代码
第一种情况:
当一个服务器启动的时候首先会投给自己一票,然后判断是不是有超过集群中半数机器的票数了,如果没有的话就编程LOOKING状态,这个状态表示集群中还没有投出leader并且自己不是leader。
当第二台服务器启动的时候依然先给自己来一票,然后会和第一台启动的服务器比较两者的SID,SID小的会将自己的所有票投给SID大的服务器。 如果SID大的服务器票数超过了一半那么它就成了Leader,后面启动的服务器自动变成follower,如果没有超过一半票数的话重复之前的步骤。
第二种情况:
第二种情况又分类两个小情况,比如集群中有五台服务器,有一台服务器可能因为网络问题和集群中的其他服务器失联了,这个时候它不会认为自己出了问题而是认为leader挂了,其实leader没有挂,那么这个机器进行选举流程,当选举的时候集群会告诉它leader是哪个,然后它就知道leader是哪个了,自己变成follower。
第二种的话就是leader真的挂了,那么会将剩下的follower进行选举流程,每个follower都有这样的一个列表(EPOCH,ZXID,SID)
然后会将Epoch最大的follower选举为leader,如果epoch都一样选择zxid最大的,如果zxid也一样的话,选举SID最大的。
至此我们说完了zookeeper集群中leader的选举流程。
四、ZAB协议
但是Zookeeper集群中依然还有一个问题,那就是Follower中的数据可能会和Leader中的数据出现数据不一致的问题。为了解决这个问题所以出现了ZAB协议。ZAB协议的原理是CAP&BASE原理。保证了最终一致性。
ZAB协议有两种模式: 一、消息广播模式 二、崩溃恢复模式
先来说一下消息广播模式
首先需要说明,集群中只有leader可以进行写操作,follower只能进行读操作,即使follower收到了客户端的写操作,它也是转给leader进行处理。
流程如下:
1、客户端发来一个写请求后,leader将这个请求转成一个Proposal(提案),这个提案有一个唯一的ID
2、leader将这个提案发送给所有follower的队列,每个follower对应一个自己的队列
3、follower从队列中取出这个Proposal然后和自己本地的Proposal的ID进行比较,如果大于自己的Proposal ID的话会先将自己本地的Proposal ID更新,然后给leader响应ACK,表示我接收你的提案。否则拒绝,不会给leader响应ack
4、当leader收到超过半数的ack后,就认为提案通过,会给所有的follower发送commit,也就是真正的提交这个Proposal
对于上述的消息广播模式,我自己认为可以理解为有leader和follower的paxos算法,思想是一样的。
崩溃恢复模式
leader发生崩溃或者因为网络问题leader与超过半数的follower失去了联系,就会进入崩溃恢复模式
崩溃恢复模式必须满足下面两个条件:
1.如果leader已经 commit了Proposal的话,必须所有的follower服务器都commit了Proposal
2.如果leader只是提出了提案,也就是第一个阶段的时候,就将这个消息丢弃。
这样的目的是为了保证数据的一致性。在崩溃之后选举leader的时候,会选择ZXID最大的follower为新的leader,这也是为了避免Proposal的提交和丢弃检查工作。