- zk的数据同步原理?
- zk的集群会出现脑裂的问题吗?
- zk的watch机制实现原理?
- zk是如何保证一致性的?
- zk的快速选举leader原理?
- zk的典型应用场景
- zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?
- zk对事物的支持?
1. zk的数据同步原理?
zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式
- peerLastZxid:Learner服务器(Follower或observer)最后处理的zxid
- minCommittedLog:leader节点proposal缓存队列的最小zxid。
- maxCommittdLog:leader节点proposal缓存队列的最大zxid。
zk的数据同步主要有以下几种类型: - DIFF:直接差异化同步
peerLastZxid介于minCommittedLog和maxCommittdLog之间, - TRUNC + DIFF:先回滚在差异化同步
如果leader发现follower节点上存在自己没有的事务记录,会先让learner回滚到leader上存在的 - TRUNC:仅回滚同步
peerLastZxid大于maxCommitterLog,leader会要求learner先回滚到maxCommittdLog对应的事务记录。 - SNAP:全量同步
peerLastZxid小于minCommittedLog。
2. zk的集群会出现脑裂的问题吗?
- 什么是脑裂?
脑裂是指,在分布式系统中,因网络问题或其他故障导致系统被切割成多个相互独立的子系统,每个子系统都独立的选择了自己的leader,当这种情况发生时,原本应该由单一领导节点控制的集群突然出现了多个领导者,导致集群数据不一致和操作冲突。 - 解决脑裂的策略
为了有效的防止脑裂,很多分布式系统采用了“过半原则”进行领导者选举,这一机制要求在选举过程中,候选节点必须获得过半的节点支持,才能成为leader。zookeeper就是通过这种方式来避免脑裂问题。 - 场景分析
假设一个zk集群由6台服务器组成,分别存放在2个机房。
由于zookeeper的机制,必须获得超过半数节点的支持才能成为leader,因此必须有6/2 + 1 = 4台。至少有4台支持才能成为leader。
1、两个机房,每个机房都有3个节点
当出现机房1和机房2网络故障导致无法通信时,由于每个机房都只有3个节点,都无法构成集群的最小单元,任一机房都无法出现leader,从而避免了脑裂的问题。
2、两个机房,机房1有4个服务节点,机房2有2个服务节点
- 当网络正常时,整个集群可以选择一个leader。
- 当机房1和机房2网络异常时,由于机房1包含4台服务器,可以正常的选择一个leader出来,而机房2只有2个服务器,无法构成leader选举的最小单元,因此保证了整个集群在任何时刻都只有一个leader的情况。
3. zk的watch机制实现原理?
- 客户端注册watcher事件到服务端,并将watcher对象保存到客户端的Watcher管理器中。
- 服务端缓存对应的节点路径和watcher事件
- 服务端的节点发生变更时,会检查节点是否绑定watcher事件
- 如果绑定则回调相应的客户端
- 客户端收到服务端发生的watcher通知事件后,会从客户端维护的Watcher管理器中找到对应的处理器进行逻辑处理。
Watcher特点:
-
watcher是一次性的,一旦触发就会移除,再次使用时需要重复注册。
-
WatchEvent是最新的通信单元,结构上只包括节点路径、事件类型、通知状态,并不会告知数据节点变化的前后内容。
-
Watcher会在当前session彻底失效时才会失效;
-
客户端watcher回调是串行同步执行的。
参考内容:https://blog.csdn.net/huxiang19851114/article/details/114079324
4. zk是如何保证一致性的?
zk非强一致性,为最终顺序一致性
zk写请求的步骤如下所示
-
客户端发送写请求到zookeeper 服务节点,如果是follower节点,则将写请求转发给leader节点执行。
-
leader节点会将写请求转成一个Proposal提案,并给此提案生成一个zxid,并且将此提案写入磁盘事务日志中(性能考虑,先写入到系统缓存中,在合适的时机在写入到磁盘中),然后发送给所有的follower节点。
-
每个follower节点都有维护一份先进先出队列,Proposal提案会进度此队列中,来保证消息的顺序执行,follower节点收到Proposal提案后,会讲Proposal提案写入到磁盘事务日志中(性能考虑,先写入到系统缓存中,在合适的时机在写入到磁盘中),写入成功后,会给leader节点 ack响应。
-
leader节点收到ack响应的数量超过集群中follower节点的一半时,就认为Proposal提案提交成功,此时会将数据封装为ZNode写到内存中,并发送commit命令给所有的follower节点,当follower节点收到commit命令后,会将数据封装成Znode节点写入内存中。
-
此时如果有follower节点写入失败(网络原因导致没收到请求、节点故障)。zookeeper有一套数据恢复机制来保证数据的最终一致性。
参考链接:https://www.jianshu.com/p/9a2ba7278f60 https://blog.csdn.net/qq798280904/article/details/129965877
崩溃恢复机制
leader服务器由于网络或者节点故障问题导致与超过一半的follower节点失去连接时,zookeeper集群会快速的选举一个新的leader,并完成新leader节点与其他节点的数据一致。
崩溃恢复主要包括2部分:leader选举和数据同步。
leader选举:
当leader宕机之后,会有两种异常情况,一个事务被leader封装成了proposal发送给了所有的follower,并且过半的follower都响应了ack,但是:
- leader在commit消息发送之前宕机(leader本身还没提交事务)
- leader在commit消息发送之后宕机(leader已经提交了事务)
因此要保证数据一致性要满足下面两个要求:
- 响应ack的follower节点舍弃被leader提出但是未提交的提案。(leader在commit消息发送之前宕机)
- 所有follower节点提交leader节点commit的提。( leader在commit消息发送之后宕机)
所以ZAB协议需要保证选举出来的新leader需要满足下面的条件。
- 新选举出来的leader不能包含未提交的提案。
- 新选举出来的leader节点中包含最大的zxid(保证数据最新)
根据以上条件选举leader。
选举出leader后就进行数据同步。
此时leader已经拥有全局最大的zxid,数据同步就是根据leader的事务日志对follower节点进行数据同步。
- leader节点根据follower节点发送过来的follower请求(包括了最大zxid),并相应消息,告知follower,自己是leader节点。
- leader节点根据follower的最大zxid,向follower发送更新指令
- SNAP:follower的数据太旧,leader将发送自己的快照SNAP指令给follower进行数据同步。
- DIFF:leader将zxid在follower.lastZxid至leader.lastZxid的数据发送给follower进行数据同步.
- TRUNC:当follower的zxid比leader的zxid还大时,leader发送从leader.lastZxid至follower.lastZxid的TRUNC指令给FOLLOWER来回归该数据
- follower完成数据同步后,发送ACKNETLEADER给leader。
- leader将该follower节点加入到自己的可用follower列表中。
问题1:zookeeper会不会从follower节点读到不是最新的数据?
会,因为zookeeper不是强一致性,还是最终一致性,客户端可能从follower节点读取不到follower节点还未完成commit的信息。
5. zk的快速选举leader原理
zookeeeper集群过程中,主要包括以下几个角色
leader角色:整个集群的核心,主要负责以下工作。
- 事务请求的唯一调度和处理,保证事务请求的顺序性(事务请求:增删改)
- 负责查询
follower角色 - 负责转发事务请求到leader节点
- 负责处理非事务请求。(非事务请求:查询)
- 参与leader的选举
- 参与事务请求的投票(需要半数以上的节点通过,leader才会提交数据;leader发起的请求,follower投票)
observer角色 - zookeeper 3.3版本引进的节点角色,观察者角色,负责的内容和follower基本一致,唯一的区别就是不参与任何投票,包括不参与leader的投票和事务请求的投票。通常在于不影响集群事务处理能力的前提下提升集群非事务的处理能力
- 负责转发事务请求到leader节点
- 负责处理非事务请求。(非事务请求:查询)
假设当前有三个zk节点组成的集群启动。
- server1启动,此时是无法进行和完成leader选举的,它需要等待其他服务节点的启动。server1会不断地尝试连接其他服务节点。
- server2启动,此时server1和server2建立了正常连接,开始选举leader节点。
- 初始情况,每个server节点都会发起一个投票并且都会将自己作为leader节点进行投票,投票信息包括myid(sid)和zxid,server1的投票为(1,0),server2的投票为(2,0),并且会分别将自己的投票信息发给集群中的其他节点
- 接收来自各个节点的投票,集群的每个节点收到投票后,首先会判断投票的有效性,如检查是否是本来投票(epoch),是否来自LOOKING状态的服务节点。
- 处理投票,对于收到的投票都会和自己的投票结果进行pk,pk规则如下
- 优先检查zxid,zxid大的作为leader节点
- zxid相同情况下,比较myid,myid大的作为leader节点
- 对于当前情况,server1收到server2的投票结果后,先比较zxid,由于zxid都为1,再次比较myid,server2的myid为2,大于server1的myid(1),于是server1更新自己的投票状态为(2, 0),对于server而言,他不需要更新自己的投票状态,只需要再次对集群中其他节点发生投票信息即可。
- 统计投票,每次投票后,都会统计投票信息,判断是否有超过半数的节点收到了相同的投票。
- 对于server1和server2而言,由于投票结果都为(2, 0),因为便认为已经选好了leader(server2为leader)
- 如果确定了leader之后就会开始改变节点的状态,如果是leader,就会改成LEADING状态,如果是follower,就会改成FOLLOWING状态
6. zk的典型应用场景
通过对zk中丰富的数据节点类型和watcher机制,可以实现一系列分布式应用中的核心功能,如:
- 数据发布、订阅:配置中心
- 负载均衡:提供服务者列表
- 命名服务:提供服务名到服务地址的映射
- 分布式协调/通知:watch机制和临时节点,获取各节点的任务进度,通过修改节点发出通知
- 集群管理:是否有机器退出和加入,选举master
- 分布式锁
- 分布式队列
7. zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?
zk是最终数据一致性,非强一致性。
zk实际上只有写的强一致性,没有读的强一致性。
leader保证顺序写,即所有的写请求在follower上是保持一致的,但是由于任何follower节点都可以实现读请求,有可能会读到leader已经commit的数据,但是follower节点还没有来得及完成commit数据,因此可能读取到旧的数据,我们可以在客户端读取数据(getData)之前调用sync方法,sync方法会使连接的follower节点在读取数据之前,先完成和leader的数据同步。来保证follower节点上是最新的数据。
8. zk对事物的支持
在zookeeper中,提供了一种“muti”操作的方式,允许客户端将多个指令打包成一个事务中,并一次性的提交给zookeeper服务端。zookeeper会保证整个事务中的操作要么都成功,要么都不成功,从而保证原子性。