zk的订阅会有丢消息的情况吗?
来源:9-2 Zookeeper实现配置中心的方案和代码解析

是个么有感情的杀手
2023-02-28
1回答
-
大能老师
2023-02-28
对于 ZooKeeper的订阅主要依赖于客户端 ZKClient 设置监听器,通过对节点设置Watch监听,节点的任何更新都会以通知的方式发送到Client端。
在这个过程中对于消息的可靠性可以分为两个部分:
ZooKeeper 集群端:
在集群端,数据的一致性和集群的可用性由 ZooKeeper 自身的一些机制来进行保障;如“ZAB全称Zookeeper Atomic Broadcast(ZAB,Zookeeper原子消息广播协议)”保障数据之间的一致性。而在集群故障时使用“Leader选举算法”来保障集群的可用性。所以消息在集群端是可靠的。
数据订阅端:
而订阅的丢失一般出现在数据订阅的过程。*由于ZK的watch一次性注册原因,以及client断开连接到重新连接上这一段时间差,可能导致zookeeper客户端不能够接收到完所有的ZK事件。*
原因分析:
所 有的Zookeeper读操作,包括getData()、getChildren()和exists(),都有一个开关,可以在操作的同时再设置一个 watch。在ZooKeeper中,Watch是一个一次性触发器,会在被设置watch的数据发生变化的时候,发送给设置watch的客户端。
watcher特性:
关于Watch实现
服务端
zookeeper server为每个本地client连接(不是session)维持一份watch表(map表),用于接收该client的watch请求,但不会把watch请求同步到其它节点(官方解释这样轻量化设计,好出多多…)
每次更新某个数据时,就会触发watch通知,开始遍历本地client是否有对应watch请求事件,如果有,再判断该连接是否连接正常,连接正常则立马发通知,否则跳过当前client,遍历下一个。。
Zks会启动独立线程,定时执行删除已断开连接的watcher,避免失活的watcher驻留消耗资源。
客户端
根据应用请求watch类型,向当前连接的服务器申请watch请求,异步等待请求回复。若收到对应的watch回复,则执行对应的回调后,删除该watch请求。
当连接断开时,则所有client的watcher会收到session断开事件执行回调,但其watch请求并没有并移除。
当连接恢复时,无论是连接本地服务还是切换集群其它服务节点,本地client 所有 watcher请求会收到session恢复事件,回调再次被执行,请求不会被删除。接着,client的watch请求,会重新注册到连接服务器上,继续等待watch回调。
Watch特性要点
1. 仅支持单次触发。每次正常成功触发后(网络断开重连会触发额外增加断开和恢复两次回调),需要重新注册。
2. Watch本地保存。Watch请求不会同步到集群其它服务器。
3. 连接断开时, 会执行一次watch回调。
4. client中断期间,其watch事件会被原先的服务器直接丢弃,不会历史缓存。
5. 连接恢复时,会执行一次watch回调。
6. Client重连成功,会重新注册本地watch请求。
事件丢失可能场景
1. watch触发到完成再次注册期间,服务端对应节点发生变更的事件会丢失。
由于单次触发的特性,收到事件执行回调到再次注册watch时间差没法避免,通常的做法是先注册,再获取变更的数据,可以确保获取最新的变更不丢失,没法实现保证过程事件不丢失。
2. 断网期间,服务端所发生的更新事件会全部丢失。
根据zks的watch设计机制,其watch是本地的,实时的。当事件发生时,服务端watch事件会实时发送,且不缓存。链路正常就发送,不正常就跳过,简单粗暴,效率高。所以即使 session 没有过期,重连后会话标识仍然有效,但是断开这段时间的消息是没法重新发送的。
开发建议
1. 所有watch回调,若需要一直观察该节点变化,则先注册再获取数据,可以确保不丢失最新数据,能满足基本业务场景需求。
2. Zk不适合用于连续性状态同步的业务场景
3. 断网恢复时需要做一次主动同步。
更多内容可阅读:
《从Paxos到Zookeeper : 分布式一致性原理与实践》
《ZooKeeper : Distributed process coordination》
https://zookeeper.apache.org/doc/r3.5.9/zookeeperProgrammers.html#ch_zkWatches
00
相似问题