zoukankan      html  css  js  c++  java
  • 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

    代码编写思路:

    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服务器,客户端需要基于数据的最新版本进行更新操作。

  • 相关阅读:
    洛谷 P6851 【onu】贪心
    联赛模拟测试12 C. sum 莫队+组合数
    晚间测试4 哪一天她能重回我身边 神奇建图+基环树
    联赛模拟测试11 D. 甜圈 线段树维护哈希值
    CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)
    CF788B Weird journey 题解
    联赛模拟测试10 C. 射手座之日
    晚间测试3 B. 单(single)
    CF538B Quasi Binary 思维题
    CF600E Lomsat gelral 树上启发式合并
  • 原文地址:https://www.cnblogs.com/donfaquir/p/10241375.html
Copyright © 2011-2022 走看看