ZooKeeper介绍
ZooKeeper
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。ZooKeeper包含一个简单的原语集, 提供Java和C的接口。ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
ZooKeeper原理
ZooKeeper是以Fast Paxos算法为基础的,Paxos 算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos作了一些优化,通过选举产生一个leader (领导者),只有leader才能提交proposer,具体算法可见Fast Paxos。因此,要想弄懂ZooKeeper首先得对Fast Paxos有所了解。
ZooKeeper的基本运转流程:
1、选举Leader。
2、同步数据。
3、选举Leader过程中算法有很多,但要达到的选举标准是一致的。
4、Leader要具有最高的执行ID,类似root权限。
5、集群中大多数的机器得到响应并接受选出的Leader。
Paxos算法
在paxos算法中,分为4种角色:
Proposer :提议者
Acceptor:决策者
Client:产生议题者
Learner:最终决策学习者
上面4种角色中,提议者和决策者是很重要的,其他的2个角色在整个算法中应该算做打酱油的,Proposer就像Client的使者,由Proposer使者拿着Client的议题去向Acceptor提议,让Acceptor来决策。这里上面出现了个新名词:最终决策。现在来系统的介绍一下paxos算法中所有的行为:
Proposer提出议题
Acceptor初步接受 或者 Acceptor初步不接受
如果上一步Acceptor初步接受则Proposer再次向Acceptor确认是否最终接受Acceptor 最终接受 或者Acceptor 最终不接受
上面Learner最终学习的目标是Acceptor们最终接受了什么议题?注意,这里是向所有Acceptor学习,如果有多数派个Acceptor最终接受了某提议,那就得到了最终的结果,算法的目的就达到了。画一幅图来更加直观:
Zookeeper所使用的zad协议也是类似paxos协议的。所有分布式自协商一致性算法都是paxos算法的简化或者变种。Client是使用zookeeper服务的机器,Zookeeper自身包含了Acceptor, Proposer, Learner。Zookeeper领导选举就是paxos过程,还有Client对Zookeeper写Znode时,也是要进行Paxos过程的,因为不同Client可能连接不同的Zookeeper服务器来写Znode,到底哪个Client才能写成功?需要依靠Zookeeper的paxos保证一致性,写成功Znode的Client自然就是被最终接受了,Znode包含了写入Client的IP与端口,其他的Client也可以读取到这个Znode来进行Learner。也就是说在Zookeeper自身包含了Learner(因为Zookeeper为了保证自身的一致性而会进行领导选举,所以需要有Learner的内部机制,多个Zookeeper服务器之间需要知道现在谁是领导了),Client端也可以Learner,Learner是广义的。
ZAB协议(ZooKeeper Atomic Broadcast原子消息广播协议)
zab协议所有事务请求必须由leader协调,首先leader发起proposal消息,大多数server同意后,然后leader发送commit消息。
zxid编号
低32位为计数器,客户端每次请求+1
高32位为epochID,每次选举新leader+1
状态和阶段
Looking:系统刚启动时或者Leader崩溃后正处于选举状态
Following:Follower节点所处的状态,Follower与Leader处于数据同步阶段
Leading:Leader所处状态,当前集群中有一个Leader为主进程
zookeeper主要分为5个阶段,选举,发现,同步,广播
1 选举:Looking状态中选举出Leader节点,Leader的lastZXID总是最新的
2 发现:Follower节点向准Leader推送FOllOWERINFO,该信息中包含了上一周期的epoch,接受准Leader的NEWLEADER指令,检查newEpoch有效性,准Leader要确保Follower的epoch与ZXID小于或等于自身的。
3 同步:将Follower与Leader的数据进行同步,由Leader发起同步指令,最总保持集群数据的一致性
4 广播:Leader广播Proposal与Commit,Follower接受Proposal与Commit;
选举
zab必须确保选举出来的leader具有最大的zxid(这和raft很像啊)。这里要注意一下,和raft一样,选举可能会出现两种结果:
□上一轮的多数派(此时leader zxid可能不是最大的,需要同步的时候调用trunc)
□上一轮的少数派,leader zxid真的是最大的(但是这个最大的zxid没有提交,在同步阶段提交?)
过程详解:
1、每个Follower都向其他节点发送选自身为Leader的Vote投票请求,等待回复;
2、Follower接受到的Vote如果比自己的ZXID更新时则投票,并更新自身的Vote,否则拒绝投票;
3、每个Follower中维护着一个投票记录表,当某个节点收到过半的投票时,结束投票并把该Follower选为Leader,投票结束;
恢复(发现和同步)
发现
1、leader生成新的zxid和epoch,接受follower发送来的FOllOWERINFO(含有当前节点的LastZXID)
2、leader向follower发送NEWLEADER;Leader根据follower发送过来的LastZXID根据数据更新策略向Follower发送更新指令;
同步
1、SNAP:如果follower数据太老,epoch还在上上一轮,leader将发送快照snap指令给follower同步
2、DIFF:正常同步阶段
3、TRUNC:如果Follower是上一轮的少数派(通过对比ztid),发送TRUNC指令让follower丢弃这段数据
广播
进入正常leader提交阶段,产生递增的zxid,接受半数投票后,再提交。
与raft的区别
我觉得zab和raft没有本质的区别,它们唯一的不同点就是如何处理上一个leader的残留日志。
·raft中,需要本轮iterm有日志提交后,才提交以前的
·zab中,在同步阶段全部解决
资料整理与引用
1、https://baike.baidu.com/item/zookeeper/4836397?fr=aladdin
2、https://www.cnblogs.com/endsock/p/3480093.html
3、https://www.cnblogs.com/jxwch/p/6433310.html
4、https://www.cnblogs.com/felixzh/p/5869212.html
5、https://blog.****.net/liu857279611/article/details/70495413
6、https://blog.****.net/z69183787/article/details/54730322