本文主要介绍ZK的集群的选举和集群的工作原理。
首先我们来看一张官网提供的图片,如下:
通过上面的图片我们可以看出如下信息:
客户端可以访问任意一台集群服务器。
ZK的集群服务器中会选择一台作为领导者。
领导者管理其他从服务。
继续引用官网的如下两段描述,对集群有个初步的了解:
组成ZooKeeper服务的服务器必须相互了解。它们在内存中维护状态映像,以及持久存储中的事务日志和快照。只要大多数服务器可用,ZooKeeper服务就可用。
客户端连接到单个ZooKeeper服务器。客户端维护一个TCP连接,通过它发送请求、获取响应、获取监视事件和发送心跳。如果到服务器的TCP连接中断,客户机将连接到另一台服务器。
好了,问题来了:
问题一:既然客户端能对所有服务器访问,那么ZK集群要保证数据的一致性是怎么实现?
问题二:ZK通过领导者管理其他服务器,即ZK集群是中心化的设计思想,既然是中心化,那ZK必然要维护中心化的可靠性,这是怎么实现的?
问题三:上面说了事务日志和快照,这一猜就是用来做数据恢复用的,那ZK具体怎么实现的?
问题四:客户端进行连接,如果一个服务器挂掉了,虽然可以连接到另一台服务器,但连接的会话还存在吗?
带着这4个问题,我们来一起了解ZooKeeper集群。
一、集群工作原理
1.1、ZooKeeper的选举机制
当集群启动或当前Leader节点故障时,所有节点进入选举状态(LOOKING)。选举基于
选票规则
每个节点投票包含三个关键字段:myid(节点ID)、ZXID(最新事务ID)、epoch(当前任期)。
投票优先级: 优先比较ZXID(数据越新越优先),若ZXID相同则比较myid(数值大的胜出)。
半数机制: 选举时,每个节点会投出自己的一票,当某个节点获得超过半数节点的投票时,它就会成为领导者。
选举过程
初始化选举:集群启动时,每个节点都认为自己是领导者,向其他节点发送投票信息,包含自己的服务器 ID 和事务 ID 等。
比较投票:节点收到其他节点的投票后,会根据规则进行比较。优先比较事务 ID,事务 ID 大的更优先成为领导者;若事务 ID 相同,则比较服务器 ID,ID 大的优先。
确定领导者:当某个节点收到超过半数节点的投票认可 它为领导者时,选举结束,该节点成为 Leader,其他节点成为 Follower 或 Observer。
状态同步:新Leader与Followers同步数据,确保状态一致。
示例: 假设集群有3个节点(myid=1,2,3),ZXID均为0,节点1、2、3均投票给自己。节点3的myid最大,最终节点3成为Leader。
各角色的介绍:
Leader
事务处理:负责处理集群中的所有事务请求,如创建、更新和删除 Znode 等操作。 Leader 接收到客户端的事务请求后,会将其转化为事务提案,并分发给集群中的其他节点进行投票表决,只有当大多数节点同意后,事务才会被提交。
选举协调:在集群启动或 Leader 出现故障时,负责领导选举过程,确保集群能够快速、稳定地选出新的 Leader 。在选举过程中,Leader 会根据节点的 ID、事务 ID 等信息来确定哪个节点应该成为新的 Leader。
集群管理:维护集群的状态信息,包括节点的连接状态、数据同步情况等 。同时,Leader 还会向 Follower 和 Observer 发送心跳消息,以确保它们与集群保持连接,并及时处理节点的加入和离开等情况。
Follower
事务处理:接收 Leader 发送的事务提案,并参与投票表决。 Follower 会根据自己的状态和事务日志来判断是否同意该提案,如果同意,则向 Leader 发送投票响应。在事务提交阶段,Follower 会根据 Leader 的指示来更新自己的数据存储。
选举参与:在选举过程中,Follower 会向其他节点发送自己的选举投票,并接收其他节点的投票信息。 根据收到的投票结果,Follower 会确定是否选举出了新的 Leader,并更新自己的状态。
数据同步:与 Leader 保持数据同步,通过接收 Leader 发送的更新日志来更新自己的数据存储。 Follower 会定期检查自己与 Leader 的数据一致性,如果发现不一致,会主动向 Leader 请求数据同步。
Observer
数据同步:与 Follower 类似,Observer 也会从 Leader 同步数据,以保持与集群数据的一致性。
事务处理:Observer 不参与事务的投票过程,只接收 Leader 发送的事务提案和提交通知,并更新自己的数据存储。 因此,Observer 可以在不影响集群事务处理性能的情况下,为客户端提供只读服务,增加集群的读处理能力。
1.2、原子广播协议
ZAB(ZooKeeper Atomic Broadcast,ZooKeeper原子广播协议) 是参考Paxos专为zookeeper设计的数据一致性 协议,是 ZooKeeper 保证数据一致性的核心算法。ZAB关注点是数据一致性,无关数据的准确性、权威性、实时性。(这也是问题一的回答)
它主要支持崩溃恢复和消息广播两大功能。
崩溃恢复(Recovery Phase): 当集群启动或 Leader 故障时,触发崩溃恢复阶段,确保数据一致性。
步骤1:所有节点进入 LOOKING 状态,发起投票,选举新 Leader。
步骤2:数据同步(Synchronization)确保所有 Follower 节点的数据状态与 Leader 节点一致,使集群进入一个一致的状态,为后续的正常操作做好准备。
Followers 将本地最大的 ZXID 发送给 Leader。
Leader 找出所有节点中最大的 ZXID,并同步缺失的事务。
消息广播(Broadcast Phase): 当集群中的节点完成同步后,进入广播阶段,用于处理客户端的事务请求,保证事务在集群中的原子性和一致性传播。
所有事物请求转发给leader.
Leader分配全局单调递增事物id(Zxid),广播事物提议
Follower处理提议,做出反馈
Leader收到过半数反馈,广播Commit
Leader做出响应
1.2.1、ZBA的崩溃恢复
Leader服务器出现崩溃,或者说由于网络原因导致Leader服务器是去了与过半Follower的联系,那么就会进入崩溃恢复模式。
根据ZAB,虽然Zxid在Leader中产生,但是这个事务成功需要大多数从服务器同意(也就是这个Zxid已经同步给大多数从服务器),所以就算Leader挂了,肯定在从服务器中大多数Zxid是和主一样的。
ZAB协议规定如果一个事务Proposal在一台机器上被处理成功,那么应该在所有的机器上都被处理成功(因为根据ZAB的第4点,收到半数反馈才广播,有一台机器成功,其他机器一般也就收到了),哪怕机器出现故障崩溃。
ZAB协议确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交(从服务器会同步数据)。
ZAB协议确保丢弃那些只在Leader服务器上被提出的事务(Leader收到事务,还没通知其他服务器同步就挂掉了,这种情况就会丢弃)。
ZAB协议需要设计的选举算法应该满足:确保提交已经被Leader提交的事务Proposal,同时丢弃已经被跳过的事务Proposal。
如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最高ZXID的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的提案。同时,如果让具有最高编号事务Proposal的机器来称为Leader,就可以省去Leader服务器检查Proposal的提交和丢弃工作的这一步操作。
数据同步
按照上文1.1,ZooKeeper的选举机制选举出Leader后,需要对节点数据进行同步。
确定同步点: 新选举出的 Leader 会根据自身的事务日志确定一个数据同步点。这个同步点通常是 Leader 上最新的、已经提交的事务 ID(zxid)。
同步数据:
Leader 会向 Follower 发送包含同步点信息的消息,通知 Follower 进行数据同步。
Follower 收到消息后,会根据自己的事务日志,将落后于同步点的事务进行回滚或补齐,使自己的数据状态与 Leader 保持一致。
切回广播模式
完成同步:当所有的 Follower 都完成数据同步后,它们会向 Leader 发送确认消息。
进入广播模式:Leader 收到超过半数 Follower 的确认消息后,认为集群已经完成恢复,可以切换回消息广播模式。此时,Leader 开始正常处理客户端的写请求,并通过消息广播将事务消息发送给 Follower,Follower 接收并执行这些事务,以保证集群数据的一致性。
1.2.2、ZAB的丢弃事务Proposal处理
在ZAB协议的事务编号ZXID设计中,ZXID是一个64位的数字,低32位是一个简单的单调递增的计数器,针对客户端的没一个事务请求,Leader服务器在产生一个新的事务Proposal的时候,都会对该计数器进行加1操作;高32位代表了Leader周期纪元的编号,每当选举产生一个新的Leader服务器,就会从这个Leader服务器上取出其本地日志中最大事务Proposal的ZXID,并从该ZXID中解析出对应的纪元值,然后对其进行加1操作,之后就会以此编号作为新的纪元,并将低32位置0开始生成新的ZXID。
基于这样的策略,当一个包含了上一个Leader周期中尚未提交过的事务Proposal服务器启动加入到集群中,发现此时集群中已经存在Leader,将自身以Follower角色连接上Leader服务器之后,Leader服务器会根据自己服务器上最后被提交的Proposal 来和Follower服务器的Proposal进行比对,发现Follower中有上一个Leader周期的事务Proposal时,Leader会要求Follower进行一个回退操作——回退到一个确实已经被集群中过半及其提交的最新的事务proposal.
1.2.3、ZAB协议与Paxos算法的区别
应用场景
ZAB 协议:专门为 Zookeeper 设计,用于解决 Zookeeper 在分布式环境下的数据一致性问题,特别是针对 Zookeeper 的主从架构和数据读写特点进行了优化 。
Paxos 算法:是一种通用的分布式一致性算法,适用于各种需要在分布式系统中达成一致性的场景,如分布式数据库、分布式存储等。
节点角色
ZAB 协议:Zookeeper 集群中有明确的领导者(Leader)和追随者(Follower)角色之分。 领导者负责接收和处理客户端的事务请求,并向追随者广播事务消息;追随者主要负责接收领导者的消息,进行数据同步和事务执行。
Paxos 算法:在 Paxos 算法中,节点没有固定的角色,每个节点都可以成为提议者(Proposer)、接受者(Acceptor)和学习者(Learner)。不同的节点在不同的阶段可能会扮演不同的角色,通过相互之间的消息交互来达成一致性。
一致性保证
ZAB 协议:ZAB 协议通过严格的顺序性保证数据的一致性 ,即所有事务按照其 zxid 的顺序在集群中进行广播和执行。它确保了在正常情况下,集群中的所有节点都能以相同的顺序看到事务的提交,从而保证了数据的一致性。
Paxos 算法:Paxos 算法通过多数派投票的方式来保证一致性。在算法执行过程中,提议者提出的提案需要得到多数接受者的批准才能被选定为最终的决议。只要多数节点达成一致,就可以保证系统的一致性,但并不严格保证事务的执行顺序与提议顺序一致。
故障恢复
ZAB 协议:ZAB 协议的崩溃恢复过程相对复杂,需要选举新的领导者,并进行数据同步。在恢复过程中,会根据节点的日志信息和 zxid 来确定哪些事务需要提交,哪些需要回滚,以保证集群数据的一致性。
Paxos 算法:Paxos 算法本身没有明确的故障恢复机制,它主要关注的是在正常运行情况下如何达成一致性。在实际应用中,通常需要结合其他机制来处理节点故障和恢复问题。
消息复杂度
ZAB 协议:ZAB 协议的消息类型相对较少,主要包括事务消息、确认消息、心跳消息等。消息的结构和交互相对简单,主要围绕领导者与追随者之间的事务广播和数据同步进行。
Paxos 算法:Paxos 算法的消息类型较多,包括提议消息、批准消息、学习消息等。在算法执行过程中,节点之间需要进行多次消息交互和复杂的逻辑判断,消息复杂度相对较高。
二、问题回答
上文事务快照和会话部分没时间写了,先这样吧,后续再补。
补: 《 Zookeeper系列:会话管理机制》
问题一:既然客户端能对所有服务器访问,那么ZK集群要保证数据的一致性是怎么实现?
通过ZAB协议保证数据一致性。
问题二:ZK通过领导者管理其他服务器,即ZK集群是中心化的设计思想,既然是中心化,那ZK必然要维护中心化的可靠性,这是怎么实现的?
ZK保证领导者的可靠性做了很多处理,如下:
心跳检测: Followers通过心跳检测Leader存活。若超时未收到心跳,触发重新选举。
ZAB 协议的恢复模式: 在 ZooKeeper 集群启动或 Leader 故障时,会进入恢复模式,确保集群数据的一致性和完整性,为选举出可靠的领导者奠定基础。
领导者选举的条件约束: 选举过程中,优先选择具有最高事务 ID 的节点作为领导者。这保证了新的领导者拥有最新的事务数据,从而能够正确地处理后续的客户端请求,避免数据丢失或不一致。
多数派确认机制: 领导者在进行数据提交等关键操作时,需要等待超过半数的 Follower 节点确认。这确保了数据在大多数节点上得到了正确的处理,提高了领导者决策的可靠性。
防止脑裂(Split-Brain): 多数派原则也能确保网络分区时仅拥有多数节点的一方能选举新Leader,避免双主。
问题三:上面说了事务日志和快照,这一猜就是用来做数据恢复用的,那ZK具体怎么实现的?
事务日志:ZooKeeper 节点会将所有的事务操作记录在事务日志中,包括事务的 zxid、操作类型、数据内容等。在数据恢复时,会从日志中读取事务记录,按照 zxid 的顺序重新执行事务,以恢复到正确的数据状态。
快照:为了减少数据恢复时的日志回放时间,ZooKeeper 会定期对内存中的数据状态进行快照,并将快照数据持久化到磁盘。在恢复时,首先加载最新的快照数据,然后再回放快照之后的事务日志,快速恢复数据。
问题四:客户端进行连接,如果一个服务器挂掉了,虽然可以连接到另一台服务器,但连接的会话还存在吗?
服务器建立连接后,会有一个会话超时时间。当服务器挂掉后,客户端会自动尝试连接到其他可用的服务器节点。在重连过程中,客户端会携带原来的会话 ID 等信息。服务器在收到客户端的重连请求后,会根据会话 ID 查找对应的会话信息。如果会话尚未超时,服务器会恢复会话状态,使客户端能够继续使用原来的会话进行操作,保证了会话的存续。
全部评论