转自: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代码:

 public class TestSetData implements Watcher {

 // 屏障,计数器
private static CountDownLatch downLatch = new CountDownLatch(1); private static ZooKeeper zookeeper = null; public static void main(String[] args) throws Exception { zookeeper = new ZooKeeper("10.0.227.66:2181", 5000, new TestSetData()); System.out.println("zookeeper.getState()1 : " + zookeeper.getState()); try {
downLatch.await();// 在计数器未归零之前,所有线程等待
} catch (Exception e) {
e.printStackTrace();
} System.out.println("zookeeper.getState()2 : " + zookeeper.getState()); zookeeper.create("/cyx", "ccc".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zookeeper.getData("/cyx", true, null); // 第一次设置
Stat stat = zookeeper.setData("/cyx", "456".getBytes(), -1);
System.out.println(stat.getCzxid() + " , " + stat.getMzxid() + " , " + stat.getVersion()); // 第二次设置
Stat stat2 = zookeeper.setData("/cyx", "789".getBytes(), -1);
System.out.println(stat2.getCzxid() + " , " + stat2.getMzxid() + " , " + stat2.getVersion()); // 获取第一次设置得到version,进行更新
try { zookeeper.setData("/cyx", "123".getBytes(), stat.getVersion()); } catch (Exception e) {
e.printStackTrace();
} } @Override
public void process(WatchedEvent event) { System.out.println("receive watched event : " + event); if (KeeperState.SyncConnected == event.getState()) { if (EventType.None == event.getType() && null == event.getPath()) {
downLatch.countDown();// 计数器-1
}
}
}
}

输出结果:

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

代码编写思路:

  1. 首先创建节点"/cyx"节点,设置节点参数"ccc"
  2. 接着我们第一次setData,更新节点参数为"456",同时获取stat,此时version已经改变
  3. 然后我们再次setData,更新节点参数为"789",同时获取stat,version也改变了
  4. 接着我们使用第一次获取的version版本号,去setData。
  5. 然后就抛异常,因为第二次setData的时候,版本号已经更新,这时候,我们拿第一次的更新的版本号去更新,是没法成功的。

解释下setData时的"-1"

在ZooKeeper中,数据版本都是从0开始计数额,所以严格的讲,"-1"不是一个合法得到数据版本,它仅仅是一个标示符。

如果客户端传入的版本参数是"-1",就是告诉zookeeper服务器,客户端需要基于数据的最新版本进行更新操作。

zookeeper_节点数据版本号问题的更多相关文章

  1. .Net客户端监听ZooKeeper节点数据变化

    一个很简单的例子,用途是监听zookeeper中某个节点数据的变化,具体请参见代码中的注释 using System; using System.Collections.Generic; using ...

  2. C# 对 App.config的appSettings节点数据进行加密

    .NET平台下的Winform和Asp.net的配置文件默认都是明文保存的,本文使用的是.Net自身如何加密配置文件,不包含自定义的加密规则 但.Net是提供了直接对配置文件加密的功能的,使用.Net ...

  3. zTree实现获取一级节点数据

    zTree实现获取一级节点数据 1.实现源码 <!DOCTYPE html> <html> <head> <title>zTree实现基本树</t ...

  4. openLDAP安装时无法操作根节点数据,提示的是This base cannot be created with PLA.

    1.无法操作根节点数据,提示的是This base cannot be created with PLA. 解决办法 1)添加一个base.ldif文件,里面的dc和配置文件里的保持一致即可 dn: ...

  5. javascript读取xml文件读取节点数据的例子

    分享下用javascript读取xml文件读取节点数据方法. 读取的节点数据,还有一种情况是读取节点属性数据. <head> <title></title> < ...

  6. ztree插件的使用及列表项拖拽的实现(jQuery)+异步加载节点数据

    为了实现如图所示的树状结构图,并使列表项可拖动到盒子里,研究了ztree这个插件的使用,并仔细研究了列表项的拖动事件.完成了预期需求,对jQuery的运用得到了提高.这个插件的功能非常强大,除了基本的 ...

  7. C#XML格式字符串取节点数据

    XML格式的字符串: <xml><return_code><![CDATA[{0}]]></return_code><return_msg> ...

  8. 用LINQ获取XML节点数据

    Insus.NET想对<从字符串中获取XML节点数据> http://www.cnblogs.com/insus/p/3299052.html 这篇改写为使用LINQ的方法实现.LINQ中 ...

  9. 从字符串中获取XML节点数据

    从字符串中获取XML节点数据,前一篇<字符串创建XML文档> http://www.cnblogs.com/insus/p/3298579.html 是储存为一个XML文档.现在,Insu ...

随机推荐

  1. 【转】Android随笔之——PackageManager详解

    参考:http://www.cnblogs.com/xingfuzzhd/p/3374504.html 今天要讲的是PackageManager.Android系统为我们提供了很多服务管理的类,包括A ...

  2. Linux无法识别Broadcom 802.11abgn无线网卡

    折腾了好久,都无法解决 索性后来直接使用的usb外置网卡,勉强能用(使用极其不便) 最后使尽浑身解数,冲着萤火般的希望,好在没有放弃 正解就是下面这   完成重启即可 sudo cp /sys/fir ...

  3. 常用命令 tcl & shell

    TCL 常用命令: 1. 当前时间  [exec  date  +%m%d_%H%M]   (实际是调用shell命令 date),比如在 icc 中保存cell 时可以用:save_mw_cel   ...

  4. 使用Ceph集群作为Kubernetes的动态分配持久化存储(转)

    使用Docker快速部署Ceph集群 , 然后使用这个Ceph集群作为Kubernetes的动态分配持久化存储. Kubernetes集群要使用Ceph集群需要在每个Kubernetes节点上安装ce ...

  5. 预编译指令#ifdef #endif

    这个是C中的.意思是说如果你定义了某个东西,则执行一段代码,这段代码是包含在ifdef到endif之间的.比如,你debug一个程序,但是到最后你需要将debug的代码删掉,很多则很麻烦.但是如果你那 ...

  6. Mysql(压缩包)下载与安装

    第一步:百度搜索  MySQL 点击官网进入 或者复制链接进入下载页面:https://downloads.mysql.com/archives/community/    第二步:选择自己需要的  ...

  7. ubuntu终端下快捷键之--字体放大缩小

    1.快捷键 Ctrl  -   字体缩小 Ctrl  + (有的电脑是“Ctrl Shift +” 三个按键同时按下)字体放大 Ctrl 0  恢复默认字体 2.终端设置默认字体 在终端下,点击右键- ...

  8. JavaEE笔记(二)

    查询load()和get()的区别 # 以下查询都是根据id查询 // Load和Get都会在第一次查询的是创建一个一级缓存查询语句 // 下一次查询的时候从缓存中查询是否有缓存的语句 // 如果有只 ...

  9. matplotlib绑定到PyQt5(有菜单)

    稍微复杂地实现matplotlib绑定到PyQt5(有菜单) [知识点] import matplotlib matplotlib.use("Qt5Agg") [效果图] [源代码 ...

  10. JAVA基础 XML生成与解析和String包装类下 .replace方法的使用以及char和字符序列的使用场景

    ptLink0.setText(arbu.getPtLink().replace("&","&")); // 如果像 '&','& ...