拜占庭问题
1982年 ,Lamport与另两人共同发表了论文提出了一种计算机容错理论,为了描述这个理论中的问题,假设了一个问题相关的故事场景,如下:
拜占庭帝国有许多支军队,不同军队的将军之间必须制订一个统一的行动计划,从而做出进攻或者撤退的决定,同时,各个将军在地理上都是被分隔开来的,只能依靠军队的通讯员来进行通讯。然而,在所有的通讯员中可能会存在叛徒,这些叛徒可以任意篡改消息,从而达到欺骗将军的目的。
这就是著名的拜占庭问题,而这个问题就和分布式场景下异步系统和不可靠的协议中达到一致性类似,后来Lamport 在 1990年提出了一个理论上的一致性解决方案,茼时给出了严格的数学证明,Paxos算法由此而生。
Paxos算法
我们首先来看一下,对于一个一致性算法来说,我们应该满足哪几点:
1.所有提出的提案最终只有一个被选定
2.如果没有提案,那么最终没有任何选定的提案
3.当提案被选定后,所有客户端都应该能获取到提案信息
在Paxos算法中,有Proposer、Acceptor以及Learner三种角色,彼此之间通过收发消息来实现通信。在分布式场景下,Acceptor绝不可能只有一个实例存在,防止出现单机故障,因此我们需要满足,多个Acceptor的情况下完成选举操作,因此我们可以指定Proposer都向一个Acceptor集合中发送提案,而这个集合中的所有的Acceptor都可能批准提案,当有足够多的Acceptor批准提案的时候,我们就认为这个提案被选定,而所谓的足够多,只需要满足当前Acceptor集合中的大半Acceptor批准提案即可,并且我们规定每个Acceptor在一次选举过程中,只可以批准一个提案。
算法过程
整个Paxos算法的过程可以总结为两阶段提交的算法执行过程,分为Prepare请求阶段和Accept请求阶段,大体的过程如下:
Prepare阶段:
Proposer会选择一个提案,并且编号为M0,然后向Acceptor集合中发送M0编号的Prepare请求,如果这个时候Acceptor集合中的某个Acceptor收到了这个请求,并且编号M0比当前已经相应的所有的提案的最大编号还要大,那么Acceptor就需要反馈自己处理的最大编号,并且不会再去响应低于M0编号的任何请求。如果没有响应过请求,则会直接响应当前的M0编号的请求。
Accept阶段:
同样的,当Proposer发出的提案请求收到了来自整个Acceptor集合中的过半Acceptor的响应,那么就代表该提案可以生成,这个时候如果响应中过半是无任何提案信息的,则代表当前的提案的取值可以是任意值,如果响应中过半是其他的提案信息,那么则从中找到最大编号的提案的值,这里称为V0,组成**[M0,V0]的Acceptor请求,再次发送给整个Acceptor集合。这个时候Acceptor如果没有通过大于v4sp** 编号的提案,那么则会视为自动同意该提案,同样如果得到了过半数的同意,则当前提案视为通过。
当然在整个过程中,每个Proposer都有自己的周期定时生成不同的提案,并且严格按照上述过程运行,最终一定能保证算法的正确性,同样的,如果Proposer已经要生成更大编号的提案,Accept将收到的最大的编号信息通知给上一个同意的最大提案的Proposer,让其放弃自己的提案,选择最新的最大编号的提案即可。
Zab协议
看了前面的Paxos算法,我们可能会认为Zookeeper就是基于Paxos算法的实现,但是事实上,Zookeeper并不是完全采用的Paxos,而是一种名为Zookeeper Atomic Broadcast,简称ZAB协议的一种支持奔溃恢复的协议作为数据一致性核心算法。ZAB协议定义整个Zookeeper中关于事物消息的处理流程,大致如下:
所有的Zookeeper处理的事物请求必须仅有一个机器来协调处理,这样的机器被称之为Leader服务器,而剩下的其他Zookeeper则称之为Follower,而Leader服务器则是会将客户端的事物请求发给所有的Follwer服务器,等待所有的Follwer服务器的反馈,一旦接受到了超过半数的Follwer服务器的正常完成事物处理的反馈后,Leader服务器就会再次发送一个Commit消息给所有的Follwer服务器,要求将刚刚的事物请求进行提交
而ZAB协议有两种基本的模式,分别为崩溃恢复和消息广播。当整个框架在运行的过程中,或者是当Leader出现网络中断、崩溃重启等异常的情况的时候,ZAB协议就会进入恢复模式,来重新选举一个新的Leader服务器来处理事物请求,当新的Leader服务器选择出来,并且和过半以上的机器实现了状态同步后,ZAB的恢复模式就结束了。这个时候就可以进行ZAB的消息广播模式了,如果在这个过程中有一个新的Zookeeper加入了当前的集群中,就会启动恢复模式,直到实现了和Leader的状态同步为止。
正如上述事物处理的过程一样,Zookeeper中只有Leader可以协调处理事物请求,那么其他的Follower服务器是不是没用了呢?当然不是,在整个Zookeeper集群中,所有的服务器都可以提供对外的请求处理,唯一的区别在于,当Follower服务器接受到了事物请求以后,会转发给Leader,让Leader进行统一处理和协调而已。当然在整个集群的运行过程中会出现很多意外,例如有半数以上的Follower服务器无法与Leader进行正常通信,就会从消息广播模式进入到崩溃恢复模式,从其他的机器中选举一个新的Leader出来,同样的一个Leader的选举必须得到超过半数的机器的支持,所以当整个集群的正常服务的机器超过半数,都可以不停的模式转换,保证集群服务的稳定性,一旦出现问题的机器数量超过了集群的半数,整个集群就不再对外提供事物请求的处理,进入保护模式,仅仅可读。