ZooKeeper_8_Java操作ZK_更新数据

来源:互联网 发布:淘宝网秋冬半身裙 编辑:程序博客网 时间:2024/06/09 16:57

更新数据


客户端可以通过ZooKeeper来更新一个节点的数据内容。


同步

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参数:

version参数用于指定节点的数据版本,表名本次更新操作是针对指定的数据版本进行的。

在上一篇<获取数据getData>中我们并没有根据指定数据版本来获取数据的接口。


好,问题来了,这里指定数据版本更新的意义何在呢?


还记得并发中的"CAS"嘛?

通俗的讲"CAS":对于值V,每次更新前都会比对其值是否是预期值A,只有符合预期,才会将V原子化的更新到新值B

(原书写的,真是...看的有点困惑了..╮(╯▽╰)╭)


zookeeper的setData接口中的version参数是CAS衍化来的..

之前介绍过,zookeeper每个节点都有数据版本的概念,在调用更新操作的时候,就可以添加version这个参数,该参数可以对应于CAS

原理中对的"预期值",表明是针对该数据版本进行更新。


形象一些说:

假如有一个客户端试图进行更新操作,它会携带上次获取到的version值进行更新。

而如果在这段时间内,ZooKeeper服务器上该节点的数值恰好已经被其他客户端更新了,那么其数据版本一定也会发生变化,

因此肯定与客户端携带对的version无法匹配,于是便无法成功更新 -- 因此可以有效地避免一些分布式更新的并发问题。


我们来看代码:

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();}}@Overridepublic 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 : CONNECTINGreceive watched event : WatchedEvent state:SyncConnected type:None path:nullzookeeper.getState()2 : CONNECTEDreceive watched event : WatchedEvent state:SyncConnected type:NodeDataChanged path:/cyx197568501464 , 197568501465 , 1197568501464 , 197568501466 , 2org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for /cyx
我们捋下代码:

a. 我们首先创建节点"/cyx"节点,设置节点参数"ccc"

b. 接着我们第一次setData,更新节点参数为"456",同时获取stat,此时version已经改变

c. 然后我们再次setData,更新节点参数为"789",同时获取stat,version也改变了

d. 接着我们使用第一次获取的version版本号,去setData。

e. 然后就抛异常,因为第二次setData的时候,版本号已经更新,这时候,我们拿第一次的更新的版本号去更新,是没法成功的。


解释下setData时的"-1":

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

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

原创粉丝点击