Zookeeper面试题
1.Zookeeper是什么
Zookeeper是一种分布式协调服务,它在中间件处于鼻祖的地位,在很早分布式概念提出来的时候,Zookeeper里面就有一些非常精彩的设计,比如说分布式锁,集群选举,崩溃恢复,数据同步,以至于之后的很多中间件,比如说redis,dubbo的注册中心,kafka也需要用到zookeeper做协调。
2.在项目中用Zookeeper来干什么了
dubbo注册中心、分布式锁、统一配置管理等
统一配置管理:将配置信息写到一个ZK的Znode下,让客户端都监听这个Znode,一旦Znode的数据发生变化,就会通知监听他的所有的客户端。
-
分布式协调组件
-
分布式锁
-
无状态化的实现
3.Zookeeper的常用命令
#启动命令
#进入bin目录
./zkServer.sh start 启动zk服务器
./zkServer.sh stop 停止zk服务器
./zkServer.sh status 查看服务器的状态
./zkCli.sh 启动客户端连接zk服务器
./zkCli.sh -server 服务器IP地址 启动客户端连接指定的服务器
#zk客户端常用的命令
help 显示所有的操作命令
create path [Value] 创建节点 -e 创建临时节点 -s 创建节点名称带序号
delete path 删除节点
deleteall path 递归删除节点
set path value 设置节点具体的值
get path 获得节点的值 -s 附加次级信息
ls path 使用 ls 命令来查看当前 znode 的子节点
stat path 查看节点状态
get -w path 监听节点内容的变化
ls -w path 监听⽬录,创建和删除⼦节点会收到通知。⼦节点中新增节点不会收到通知
lw -R -w path 监听所有⼦节点中⼦节点的变化,但内容的变化不会收到通知
4.Zookeeper的持久化机制(对比Redis)
zk的数据是运行在内存中的,zk提供了两种持久化机制
- 事务日志
zk把执行的命令以日志形式保存在dataLogDir指定的路径中的文件中(没指定按dataDir指定的路径) - 数据快照
zk每隔一段时间会做一次内存数据的快照,把该时刻的内存数据保存在快照文件中
zk同时使用两种形式的持久化,先恢复快照文件的数据到内存中,再用日志文件中数据做增量恢复,这样的恢复速度更快。
5.Zookeeper的监听机制
Zookeeper允许客户端向服务端中某个Znode注册一个Watcher,当这个Znode发生改变(setData,create,delte)时候,指定客户端就会接收到异步通知。
6.Zookeeper分布式锁的实现原理
Zookeeper如何上读锁
原理:前面只有读锁的时候才可以上读锁,也就是只要最小节点是读锁,那么后面肯定都是读锁不可能是写锁,就可以加读锁成功
- 创建一个名称为read的临时序号节点,用于表示读锁
- 获取zk中序号比自己小的所有节点
- 判断最小节点是否是读锁,如果是就加锁成功
- 否则就加锁失败,监听这个最小节点,当最小的节点发生变化时就会通知当前节点,然后再去获取比自己小的所有节点,判断最小节点是否是读锁,不断循环,直到最小的节点是读锁就加锁成功
Zookeeper如何上写锁
原理:只要前面没有任何锁就可以加锁成功,也就是只要自己是最小节点就加锁成功
- 创建一个名称为write的临时序号节点,用于表示写锁
- 获取所有的子节点
- 判断自己是否是最小的节点,如果是就加写锁成功
- 否则加写锁失败,监听当前节点的上一个节点,如果上一个节点有变化就就会通知当前节点。然后再去获取所有子节点,判断自己是否是最小节点,不断的循环,直到自己是最小节点就加锁成功
7.Zookeeper集群模型
zookeeper集群是一主多从
的模型
节点分为三种角色:
- leader:主要所有请求
- follower: 只能处理读的请求,写请求转发给leader,参与选举
- observer: 只能处理读的请求,不参与选举
节点的状态有四种
- Looking :选举状态。
- Following :Follower 节点(从节点)所处的状态。
- Leading :Leader 节点(主节点)所处状态。
- Observing:观察者节点所处的状态
8.Zookeepr的选举机制
全新集群启动选举规则:
(1)服务器1启动,发起一次选举。
服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成;
服务器1状态保持为LOOKING;
(2)服务器2启动,再发起一次选举。
服务器1和2分别投自己一票,然后彼此交换投票信息后,服务器1发现服务器2的id比自己大,更改选票投给服务器2;
此时服务器1票数0票,服务器2票数2票,不够半数以上(3票),选举无法完成;
服务器1,2状态保持LOOKING;
(3)服务器3启动,发起一次选举。
与上面过程一样,服务器1和2先投自己一票,然后因为服务器3id最大,两者更改选票投给为服务器3;
此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。
服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
(4)服务器4启动,发起一次选举。
此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。
此时服务器4服从多数,更改选票信息为服务器3;
服务器4并更改状态为FOLLOWING;
(5)服务器5启动,同4一样投票给3,此时服务器3一共5票,服务器5为0票; 服务器5并更改状态为FOLLOWING;
最终Leader是服务器3,状态为LEADING;
其余服务器是Follower,状态为FOLLOWING。
理论上是事务id大的胜出,但是全新的集群启动时事务id都是0,所以只比较服务器id,投票过半数时,服务器id大的胜出
崩溃恢复时的leader选举:
- 逻辑时钟小的选举结果被忽略,重新投票
- 统一逻辑时钟后,事务id大的胜出
- 事务id相同的情况下,服务器id大的胜出
事务id:zxid越大,表示这个节点的数据越新
server id:就是我们配置集群的时候那个myid
逻辑时钟EPOCH:每次选举出leader时这个值就会+1,同一轮选举中,这个值是一致的
9.ZAB协议的特点
ZAB协议有如下特点:
- follower节点上所有的写请求都转发给leader
- 写操作严格有序
- Zookeeper使用改编的两阶段提交协议来保证各个节点的事务一致性
正常的两阶段提交是
1.协调者询问所有参与者是否可以提交
2.参与者回复yes 或 or
3.当协调者收到所有参与者yes之后才会去执行commit,否则执行rollback。
4.参与者执行完成回复yes
改编的两阶段提交协议:超过半数的参与者回复yes,而不是所有参与者回复yes
10.ZAB协议是如何保证ZK各个节点数据一致性的
zookeeper集群的状态分为两种:正常状态和异常状态。也就是有leader(能提供服务)和没有leader(进入选举)。也就是说zab协议包含两种基本模式:消息广播
和 崩溃恢复
消息广播(数据同步原理)
广播模式就是指zookeeper正常工作的模式。它是通过改编的两阶段提交协议保证各个节点的数据一致性的。
- leader从客户端或者follower那里收到一个写请求
- leader先把数据写到本地数据文件中,再以proposal的形式发送给所有follower的proposal队列中
- follower收到proposal后,也根据接收先后顺序将数据写到本地磁盘中,写入成功后发送ack给leader
- leader收到超过半数的ack消息后,leader会发送commit请求,同时自身完成事务的提交(将本地磁盘数据写到内存中)
- follower收到消息后也会将事务提交把数据写到内存中,最后反馈给客户端。
崩溃恢复
一旦leader宕机就会进入崩溃恢复模式。崩溃恢复主要包括leader选举和数据恢复两部分。
根据两阶段提交模型,有可能leader宕机会带来数据不一致情况有两种
- 如果事务没有发出去,所有follower都没有收到这个事务,leader宕机了
- leader已经commit了,但是commit消息没发出去之前宕机了
ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
leader选举
follower会用最快的速度选举出一个新的leader。会根据每个follower节点的zxid来选择,谁的zxid最大,谁的数据最新,就会被选举为新的leader。如果zxid都一样,说明在leader故障之前,所有的follower节点数据完全一致,此时会选择myid最大的节点成为leader。
如果新leader选出来之后,原来的leader又恢复了,此时原来的leader会自动成为follower,之前的事务即使重新发送给新的leader,因为新的leader开启了新的纪元,而原先的leader中的zxid还是旧的纪元,自然会被丢弃。该节点的zxid也会更新成新的纪元。
数据恢复
场景:
- 选举为新的leader后follower要跟leader之间进行同步
- 或者某个follower宕机后,leader没法把数据同步给宕机的节点,就会造成数据不一致。所以为了解决这个问题,当节点启动时,第一件事就是找当前leader同步数据
步骤:
- 新的leader跟follower建立连接,follower给leader发送自己的zxid
- leader将自己的zxid和follower发送过来的zxid进行对比
- leader确定follower的数据同步点,就是两个zxid之间的数据需要同步
- follower开始同步数据,同步时不对外提供读写服务(保证CP)
- follower同步完成后,发送消息给leader
- leader修改当前follower的状态为update,这个时候follower就可以正常接受客户端的读写请求,但是写入请求会转发给leader