ZooKeeper编程
CreateMode
PERSISTENT:创建后只要不删就永久存在
EPHEMERAL:会话结束年结点自动被删除
SEQUENTIAL:节点名末尾会自动追加一个10位数的单调递增的序号
PERSISTENT_SEQUENTIAL:结合PERSISTENT和SEQUENTIAL
EPHEMERAL_SEQUENTIAL:结合EPHEMERAL和SEQUENTIAL
package basic; import java.io.IOException; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; public class Demo { private static final int TIMEOUT = 3000; public static void main(String[] args) throws IOException { ZooKeeper zkp = new ZooKeeper("localhost:2181", TIMEOUT, null); try { // 创建一个EPHEMERAL类型的节点,会话关闭后它会自动被删除 zkp.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL); if (zkp.exists("/node1", false) != null) { System.out.println("node1 exists now."); } try { // 当节点名已存在时再去创建它会抛出KeeperException(即使本次的ACL、CreateMode和上次的不一样) zkp.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); } catch (KeeperException e) { System.out.println("KeeperException caught:" + e.getMessage()); } // 关闭会话 zkp.close(); zkp = new ZooKeeper("localhost:2181", TIMEOUT, null); //重新建立会话后node1已经不存在了 if (zkp.exists("/node1", false) == null) { System.out.println("node1 dosn't exists now."); } //创建SEQUENTIAL节点 zkp.create("/node-", "same data".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT_SEQUENTIAL); zkp.create("/node-", "same data".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT_SEQUENTIAL); zkp.create("/node-", "same data".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT_SEQUENTIAL); Listchildren = zkp.getChildren("/", null); System.out.println("Children of root node:"); for (String child : children) { System.out.println(child); } zkp.close(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
第一次运行输出:
node1 exists now.
KeeperException caught:KeeperErrorCode = NodeExists for /node1
node1 dosn't exists now.
Children of root node:
node-0000000003
zookeeper
node-0000000002
node-0000000001
第二次运行输出:
node1 exists now.
KeeperException caught:KeeperErrorCode = NodeExists for /node1
node1 dosn't exists now.
Children of root node:
node-0000000003
zookeeper
node-0000000002
node-0000000001
node-0000000007
node-0000000005
node-0000000006
注意两次会话中创建的PERSISTENT_SEQUENTIAL节点序号并不是连续的,比如上例中缺少了node-0000000004.
Watcher & Version
WatchEvent有两种类型:NodeDataChanged、NodeDeleted和NodeChildrenChanged。
调用setData()时会触发NodeDataChanged;
调用delete()时上述三种event都会触发。
要exists()中安放的Watcher监听NodeDataChanged、NodeDeleted型Event;
要getData()中安放的Watcher监听NodeDataChanged、NodeDeleted型Event;(如果这种event都被触发,比如发生了delete,那getData是收到NodeDataChanged呢还是收到NodeDeleted呢?)
要getChildren()中安放的Watcher监听NodeChildrenChanged型Event.
方法一:
package basic; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; public class Demo { private static final int TIMEOUT=3000; private static Watcher getWatcher(final String msg){ return new Watcher(){ @Override public void process(WatchedEvent event) { System.out.println(msg+"\t"+event.getType().name()); } }; } public static void main(String[] args) throws IOException{ //建立连接,触发Watcher ZooKeeper zkp=new ZooKeeper("localhost:2181",TIMEOUT,getWatcher("Connection changed")); try{ zkp.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zkp.exists("/node1", getWatcher("Existence changed")); Stat stat=new Stat(); //getData时获取version,因为在setData和delete时需要提供正确的version byte[] data=zkp.getData("/node1", getWatcher("Nodedata changed"), stat); System.out.println("NodeData is:"+ new String(data)); zkp.getChildren("/", getWatcher("Children changed")); //setData触发exists、getData中的Watcher。setData时会改为version,所以注意要获取新的version stat=zkp.setData("/node1","newdata".getBytes(), stat.getVersion()); //delete触发exists、getData、getChildren中的Watcher zkp.delete("/node1", stat.getVersion()); zkp.close(); }catch(Exception e){ System.out.println(e.getMessage()); } } }
运行输出:
Connection changed None
NodeData is:data1
Existence changed NodeDataChanged
Nodedata changed NodeDataChanged
Children changed NodeChildrenChanged
调用delete时会触发NodeDeleted,但是在上例中exists并没有监听到该事件,这正验证了Watcher是一次性的,因为在delete之前调用了setData,exists监听到NodeDataChanged事件后,它上面的Watcher就失效了。
把CreateMode改为EPHEMERAL后,即使没有显式调用setData()和delete(),运行输出也跟上面一样,因为在close()时隐式调用了delete()。
zkp.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); zkp.exists("/node1", getWatcher("Existence changed")); Stat stat=new Stat(); byte[] data=zkp.getData("/node1", getWatcher("Nodedata changed"), stat); System.out.println("NodeData is:"+ new String(data)); zkp.getChildren("/", getWatcher("Children changed")); zkp.close();
方法二:
上面是分别给exists、getData、getChildren等安装不同的Watcher。也可以整个Session上安装一个统一的Watcher,然后在调用exists、getData、getChildren时用true或false来指定要不要安装Watcher。
package basic; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; public class Demo3 { private static final int TIMEOUT=3000; private static Watcher getWatcher(){ return new Watcher(){ @Override public void process(WatchedEvent event) { System.out.println(event.toString()); } }; } public static void main(String[] args) throws IOException{ ZooKeeper zkp=new ZooKeeper("localhost:2181",TIMEOUT,null); try{ //一次性注意Watcher zkp.register(getWatcher()); zkp.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat=new Stat(); byte[] data=zkp.getData("/node1", false, stat); zkp.exists("/node1", false); zkp.getChildren("/", false); System.out.println("NodeData is:"+ new String(data)); stat=zkp.setData("/node1", "newdata".getBytes(), stat.getVersion()); zkp.delete("/node1", stat.getVersion()); zkp.close(); }catch(Exception e){ System.out.println(e.getMessage()); } } }
运行输出:
WatchedEvent state:SyncConnected type:None path:null
NodeData is:data1
我们看到调用exists、getData、getChildren时都设为false,Watcher仍然被调用了一次。
改为
byte[] data=zkp.getData("/node1", true, stat);
zkp.exists("/node1", false);
zkp.getChildren("/", false);
输出:
WatchedEvent state:SyncConnected type:None path:null
NodeData is:data1
WatchedEvent state:SyncConnected type:NodeDataChanged path:/node1
改为
byte[] data=zkp.getData("/node1", false, stat);
zkp.exists("/node1", true);
zkp.getChildren("/", false);
输出:
WatchedEvent state:SyncConnected type:None path:null
NodeData is:data1
WatchedEvent state:SyncConnected type:NodeDataChanged path:/node1
改为
byte[] data=zkp.getData("/node1", false, stat);
zkp.exists("/node1", false);
zkp.getChildren("/", true);
输出:
WatchedEvent state:SyncConnected type:None path:null
NodeData is:data1
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
改为
byte[] data=zkp.getData("/node1", true, stat);
zkp.exists("/node1", true);
zkp.getChildren("/", false);
System.out.println("NodeData is:"+ new String(data));
//stat=zkp.setData("/node1", "newdata".getBytes(), stat.getVersion());
zkp.delete("/node1", stat.getVersion());
输出:
WatchedEvent state:SyncConnected type:None path:null
NodeData is:data1
WatchedEvent state:SyncConnected type:NodeDeleted path:/node1
注意本次getData和exists收到的EventType都是NodeDeleted,而Watcher是在Session上注册的,所以Watcher只被调用了一次。有一点不解的是:为什么这次调用delete,getData收到的是NodeDeleted,而在方法一中收到的是NodeDataChanged?
改为
byte[] data=zkp.getData("/node1", true, stat);
zkp.exists("/node1", true);
zkp.getChildren("/", true);
System.out.println("NodeData is:"+ new String(data));
//stat=zkp.setData("/node1", "newdata".getBytes(), stat.getVersion());
zkp.delete("/node1", stat.getVersion());
输出:
WatchedEvent state:SyncConnected type:None path:null
NodeData is:data1
WatchedEvent state:SyncConnected type:NodeDeleted path:/node1
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
ACL