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服務器,客戶端需要基於數據的最新版本進行更新操作。

轉載於:https://www.cnblogs.com/donfaquir/p/10241375.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章