zookeeper_节点数据版本号问题
转自:Simba_cheng
更新节点数据的方法:
- 同步方法:Stat setData(final String path, byte data[], int version)
- 异步方法:void setData(final String path, byte data[], int version, StatCallback cb, Object ctx)
参数说明:
- path:指定数据节点路径
- data[]:一个字节数组,即需要使用该数据来覆盖节点现在的数据内容
- version:指定节点的数据版本
- cb:注册一个异步回调函数
- ctx:用于传递上下文信息的对象
其中:
version参数用于指定节点的数据版本,表名本次更新操作是针对指定的数据版本进行的。
指定数据版本更新的意义何在呢?
通俗的讲"CAS":对于值V,每次更新前都会比对其值是否是预期值A,只有符合预期,才会将V原子化的更新到新值B
CAS具体解释
zookeeper的setData接口中的version参数是CAS衍化来的
zookeeper每个节点都有数据版本的概念,在调用更新操作的时候,就可以添加version这个参数,该参数可以对应于CAS
原理中对的"预期值",表明是针对该数据版本进行更新。
形象一些说:
假如有一个客户端试图进行更新操作,它会携带上次获取到的version值进行更新。
而如果在这段时间内,ZooKeeper服务器上该节点的数值恰好已经被其他客户端更新了,那么其数据版本一定也会发生变化,
因此肯定与客户端携带对的version无法匹配,于是便无法成功更新 -- 因此可以有效地避免一些分布式更新的并发问题。
Demo代码:
1 public class TestSetData implements Watcher { 2 3 // 屏障,计数器 4 private static CountDownLatch downLatch = new CountDownLatch(1); 5 6 private static ZooKeeper zookeeper = null; 7 8 public static void main(String[] args) throws Exception { 9 10 zookeeper = new ZooKeeper("10.0.227.66:2181", 5000, new TestSetData()); 11 12 System.out.println("zookeeper.getState()1 : " + zookeeper.getState()); 13 14 try { 15 downLatch.await();// 在计数器未归零之前,所有线程等待 16 } catch (Exception e) { 17 e.printStackTrace(); 18 } 19 20 System.out.println("zookeeper.getState()2 : " + zookeeper.getState()); 21 22 zookeeper.create("/cyx", "ccc".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 23 24 zookeeper.getData("/cyx", true, null); 25 26 // 第一次设置 27 Stat stat = zookeeper.setData("/cyx", "456".getBytes(), -1); 28 System.out.println(stat.getCzxid() + " , " + stat.getMzxid() + " , " + stat.getVersion()); 29 30 // 第二次设置 31 Stat stat2 = zookeeper.setData("/cyx", "789".getBytes(), -1); 32 System.out.println(stat2.getCzxid() + " , " + stat2.getMzxid() + " , " + stat2.getVersion()); 33 34 // 获取第一次设置得到version,进行更新 35 try { 36 37 zookeeper.setData("/cyx", "123".getBytes(), stat.getVersion()); 38 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } 42 43 } 44 45 @Override 46 public void process(WatchedEvent event) { 47 48 System.out.println("receive watched event : " + event); 49 50 if (KeeperState.SyncConnected == event.getState()) { 51 52 if (EventType.None == event.getType() && null == event.getPath()) { 53 downLatch.countDown();// 计数器-1 54 } 55 } 56 } 57 }
输出结果:
zookeeper.getState()1 : CONNECTING receive watched event : WatchedEvent state:SyncConnected type:None path:null zookeeper.getState()2 : CONNECTED receive watched event : WatchedEvent state:SyncConnected type:NodeDataChanged path:/cyx 197568501464 , 197568501465 , 1 197568501464 , 197568501466 , 2 org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for /cyx
代码编写思路:
- 首先创建节点"/cyx"节点,设置节点参数"ccc"
- 接着我们第一次setData,更新节点参数为"456",同时获取stat,此时version已经改变
- 然后我们再次setData,更新节点参数为"789",同时获取stat,version也改变了
- 接着我们使用第一次获取的version版本号,去setData。
- 然后就抛异常,因为第二次setData的时候,版本号已经更新,这时候,我们拿第一次的更新的版本号去更新,是没法成功的。
解释下setData时的"-1":
在ZooKeeper中,数据版本都是从0开始计数额,所以严格的讲,"-1"不是一个合法得到数据版本,它仅仅是一个标示符。
如果客户端传入的版本参数是"-1",就是告诉zookeeper服务器,客户端需要基于数据的最新版本进行更新操作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!