赞
踩
ZooKeeper Atomic Broadcast,ZooKeeper原子消息广播协议。ZAB协议是为分布式协调服务ZK专门设计的一种支持崩溃恢复的原子广播协议。ZK主要依赖ZAB协议来实现分布式数据的最终一致性,基于该协议,ZK实现一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
对应于org.apache.zookeeper.server.quorum.QuorumPeer.ServerState
枚举类,ZAB协议的Java实现都是在这个包下面,下文不再写全路径名:
public static enum ServerState {
LOOKING,
FOLLOWING,
LEADING,
OBSERVING;
}
进程的可能状态:
ZK给ZAB定义的4种状态,反应ZK从选举到对外提供服务的过程中的四个步骤。状态枚举源码:
public enum ZabState {
ELECTION,
DISCOVERY,
SYNCHRONIZATION,
BROADCAST
}
启动时的状态转换
运行过程中的状态转换
Epoch指当前集群的周期号(年代号),集群的每次Leader变更都会产生一个新的周期号,周期号的产生规则是在上一个周期号的基础上加1,这样当之前的Leader崩溃恢复后会发现自己的周期号比当前的周期号小,说明此时集群已经产生新的Leader,旧的Leader会再次以Follower的角色加入集群。
提议,源码为Leader.Proposal
,是Leader的内部类,由Leader发起选举的提议:
public static class Proposal extends SyncedLearnerTracker {
private QuorumPacket packet;
protected Request request;
// 省略构造方法和getter
public long getZxid() {
return this.packet.getZxid();
}
}
投票,源码:
public class Vote {
// 默认值为0
private final int version;
private final long id;
private final long zxid;
// 默认值-1
private final long electionEpoch;
// 默认值-1
private final long peerEpoch;
// 默认值Looking
private final QuorumPeer.ServerState state;
// 省略构造方法和getter
}
该协议主要通过唯一的事务编号Zxid(ZooKeeper Transaction id)保障集群状态的唯一性。Zxid与RDBMS中的事务id类似,用于标识一次提议的id;为了保证顺序性,Zxid必须单调递增,保证全局有序。
Zxid指ZAB协议的事务编号,一个64位的数字,低32位存储的是一个简单的单调递增的计数器,针对客户端的每一个事务请求,计数器都加1。高32位存储的是Leader的周期号Epoch。每次选举产生一个新的Leader时,该Leader都会从当前服务器的日志中取出最大事务的Zxid,获取其中高32位的Epoch值并加1,以此作为新的Epoch,并将低32位重置为0,重新开始计数。
这样设计的好处是旧的Leader宕机后重启,它不会被选举为Leader,因为此时它的Zxid肯定小于当前的新Leader。当旧的Leader作为Follower接入新的Leader后,新的Leader会让它将所有的拥有旧的epoch号的未被COMMIT的Proposal清除。
ZAB协议有两种模式,分别是崩溃恢复模式(集群选主)和消息广播模式(数据同步)。
Recovery,当集群启动、集群重启、网络中断、Leader崩溃后,集群将开始选主,该过程为崩溃恢复模式。当选举产生新的Leader服务器,同时集群中已经有超过半数(ZK里定义为quorum,下同)的Follower机器与该Leader服务器完成状态(数据)同步之后,ZAB协议会退出恢复模式,即进入消息广播模式。
在ZAB协议中,为保证程序的正确运⾏,整个恢复过程结束后需要选举出⼀个新的Leader服务器。Leader选举算法不仅仅需要让Leader⾃身知道已经被选举为Leader,同时还需要让集群中的所有其他机器也能够快速感知到选举产⽣出来的新Leader服务器。
Boardcast,广播的过程实际上是一个简化的二阶段提交过程。当Leader被选举出来后,Leader将最新的集群状态广播给其他Follower,该过程为广播模式。
当一台遵守ZAB协议的服务器启动后加入到集群中,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么加入的服务器会自觉的进入数据恢复模式:找到Leader所在的服务器,并与其进⾏数据同步,数据同步完成后参与到消息⼴播流程中。
消息广播过程:
ZAB协议规定:如果⼀个事务Proposal在⼀台机器上被处理成功,则在所有的机器上都被处理成功,哪怕机器出现故障崩溃。
数据同步:所有正常运行的服务器要么成为Leader,要么成为Follower并和Leader保持同步。
ZAB的选举出来的Leader必须满足条件:
能够确保提交已经被Leader提交的事务Proposal,同时丢弃已经被跳过的事务Proposal。即:
ZAB的四个阶段:Leader Election领导选举、Discovery发现、Synchronization同步、Broadcast广播。
选举阶段
在集群选举开始时,所有节点都处于选举阶段。当某一个节点的票数超过半数节点后,该节点将被推选为准Leader。选举阶段的目的就是产生一个准Leader。只有到达广播阶段后,准Leader才会成为真正的Leader。
发现阶段
各个Follower开始和准Leader进行通信,同步Follower最近接收的事务提议。这时,准Leader会产生一个新的Epoch,并尝试让其他Follower接收该Epoch后再更新到本地,即更新acceptedEpoch。
发现阶段的一个Follower只会连接一个Leader,如果节点1认为节点2是Leader,则当节点1尝试连接节点2时,如果连接被拒绝,则集群会进入重新选举阶段。发现阶段的主要目的是发现当前大多数节点接收的最新提议。
同步阶段
主要是将Leader在前一阶段获得的最新提议信息同步到集群中所有的副本。只有当quorum都同步完成时,准Leader才会成为真正的Leader。Follower只会接收Zxid比自己的lastZxid大的提议。同步阶段完成后集群选主的操作才完成,新的Leader将产生。
广播阶段
ZK集群开始正式对外提供事务服务,这时Leader进行消息广播,将其上的状态通知到其他Follower,如果后续有新的节点加入,则Leader会对新节点进行状态的同步。ZAB提交事务并不像2PC一样需要全部Follower都Ack,只需要得到quorum的Ack就可以。
每个Server首先都提议自己是Leader,并为自己投票,然后将投票结果与其他Server的选票进行对比,权重大的胜出,使用权重较大的选票更新自身的选票箱,具体选举过程:
在选举阶段完成后,Leader通知其他Follower集群已经成为Uptodate状态,Follower收到Uptodate消息后,接收Client的请求并开始对外提供服务。
ZAB协议的Java实现与其定义略有不同,在实际实现时,选举阶段采用Fast Leader Election模式。在该模式下,节点首先向所有Server提议自己要成为Leader,当其他Server收到提议以后,判断Epoch信息并接收对方的提议,然后向对方发送接收提议完成的消息。在Java的实现过程中将发现阶段和同步阶段合并为恢复阶段。因此,ZAB协议的Java实现只有3个阶段:Fast Leader Election、Recovery和Broadcast。
简称FLE,是Java实现版的选举机制,对应源码为FastLeaderElection implements Election
。Election是一个接口,有两个方法:
public interface Election {
Vote lookForLeader() throws InterruptedException;
void shutdown();
}
选举过程核心在lookForLeader()
方法。
FLE会选举拥有最新提议的历史节点(其lastZxid最大)作为Leader,故而可省去发现最新提议的步骤。
成为leader的条件?
server_id
最大的,即zoo.cfg
中配置的myid
节点在选举开始时,都默认投票给自己,当接收其他节点的选票时,会根据上面的Leader条件判断并且更改自己的选票,然后重新发送选票给其他节点。当有一个节点的得票超过半数,该节点会设置自己的状态为Leading,其他节点会设置自己的状态为Following。
将发现阶段和同步阶段合并为恢复阶段。这一阶段Follower发送他们的lastZxid给Leader,Leader根据lastZxid决定如何同步数据。这里的实现跟前面的阶段3有所不同:Follower收到TRUNC指令会终止L.lastCommitedZxid
之后的Proposal,收到DIFF指令会接收新的Proposal。
ZAB协议的消息广播使用原子广播协议, 类似一个二阶段提交的过程 ,但又有所不同:
相同点:
不同点:
本质区别在于两者的设计目的不一样:ZAB协议主要用于构建一个高可用的分布式数据主备系统,而Paxos算法则用于构建一个分布式的一致性状态机系统。
主从架构下,Leader崩溃,数据一致性怎么保证?
Leader崩溃后,集群会选出新的Leader,进入恢复阶段,新Leader具有所有已经提交的提议,因此它会保证让Followers同步已提交的提议,丢弃未提交的提议(以Leader的记录为准),保证整个集群的数据一致性。
选举Leader时,整个集群无法处理写请求的,如何快速进行leader选举?
通过FLE实现的,Leader选举只需要超过半数的节点投票即可,这样不需要等待所有节点的选票,能够尽早选出Leader。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。