新建一个maven项目
引入依赖jar包
pom.xml引入依赖jar包,实际上还需要sla4j和log4j,但是maven会自动帮忙我们引入这里只需要引入Zookeeper的即可
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
新建一个类 - 测试连接Zookeeper服务器
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
connCountDownLatch.countDown();
}
}
public static void main(String[] args) {
try {
ZooKeeper zooKeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample());
System.out.println(zooKeeper.getState());
connCountDownLatch.await();
System.out.println("Zookeeper session established");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这里定义一个CountDownLatch ,在main方法中处于等待状态,当Zookeeper客户端连接上服务器之后解除阻塞,最终输出结果
CONNECTING
Receive watched event : WatchedEvent state:SyncConnected type:None path:null
Zookeeper session established
Zookeeper构造说明:
在Zookeeper客户端与服务端建立连接是一个异步的过程,也就是说代码Zookeeper实例化之后马上返回,并没有真正的连接上了服务器端,所以代码中加上了CountDownLatch 来进行阻塞,当连接成功之后解除阻塞,打印最后一段话
创建节点
创建节点中两种方式,同步、异步.
参数说明:
同步
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_Create_API_Sync implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
connCountDownLatch.countDown();
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zookeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample_Create_API_Sync());
System.out.println(zookeeper.getState());
connCountDownLatch.await();
String path1 = zookeeper.create("/zk-test-PERSISTENT-", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("Success create znode: " + path1);
}
}
输出结果如下:可以看到
CONNECTING
Receive watched event : WatchedEvent state:SyncConnected type:None path:null
Success create znode: /zk-test-PERSISTENT-
异步
异步方式于同步方式的区别在于节点的创建过程(包括网络通信和服务端的节点创建过程)是异步的,在同步接口调用过程中,开发者需要关注接口抛出异常的可能,但是在异步接口中,接口本身不会抛出异常,所有异常都会在回调函数中通过Result Code来体现。
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_Create_API_ASync implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
connCountDownLatch.countDown();
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zookeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample_Create_API_ASync());
System.out.println(zookeeper.getState());
connCountDownLatch.await();
zookeeper.create("/zk-test-ephemeral-", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL,
new IStringCallback(), "I am context. ");
Thread.sleep(Integer.MAX_VALUE);
}
static class IStringCallback implements AsyncCallback.StringCallback{
@Override
public void processResult(int rc, String path, Object ctx, String name) {
System.out.println("Create path result: [" + rc + ", " + path + ", " + ctx + ", real path name: " + name);
}
}
}
输出结果如下:
CONNECTING
Receive watched event : WatchedEvent state:SyncConnected type:None path:null
Create path result: [-110, /zk-test-ephemeral-, I am context. , real path name: null
异步回调参数说明:
删除节点
删除节点提供了两个方法,分别是同步和异步.
参数说明:
同步
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_DELETE_API_Sync implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
connCountDownLatch.countDown();
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zookeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample_DELETE_API_Sync());
System.out.println(zookeeper.getState());
connCountDownLatch.await();
zookeeper.delete("/zk-test-ephemeral-", 0);
}
}
异步
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_DELETE_API_ASync implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
connCountDownLatch.countDown();
}
}
public static void main(String[] args) throws Exception {
ZooKeeper zookeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample_DELETE_API_ASync());
System.out.println(zookeeper.getState());
connCountDownLatch.await();
zookeeper.create("/zk123", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zookeeper.delete("/zk123", 0 , new IVoidCallBack() , "test-delete async");
Thread.sleep(Integer.MAX_VALUE);
}
static class IVoidCallBack implements AsyncCallback.VoidCallback{
@Override
public void processResult(int rc, String path, Object ctx) {
System.out.println("rc : " + rc + " , path : " + path + " ctx : " + ctx.toString());
}
}
}
输出如下:
CONNECTING
Receive watched event : WatchedEvent state:SyncConnected type:None path:null
rc : 0 , path : /zk123 ctx : test-delete async
子节点读取
参数说明
同步
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_Children_API_Sync implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
private static ZooKeeper zookeeper = null;
private static String path = "/zk01";
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
if(event.getType() == EventType.None && null == event.getPath()){
connCountDownLatch.countDown();
}else if(event.getType() == EventType.NodeChildrenChanged){
try {
System.out.println("child change reget childs : " + zookeeper.getChildren(path, true));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
zookeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample_Children_API_Sync());
System.out.println(zookeeper.getState());
connCountDownLatch.await();
zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zookeeper.create(path + "/c1", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
List<String> childs = zookeeper.getChildren(path, true);
System.out.println(childs);
zookeeper.create(path + "/c2", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Thread.sleep(Integer.MAX_VALUE);
}
}
输出结果:
CONNECTING
Receive watched event : WatchedEvent state:SyncConnected type:None path:null
[c1]
Receive watched event : WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/zk01
child change reget childs : [c1, c2]
需要注意的是Watcher通知是一次性的,即一旦触发一次通知后,该Watcher就失效了,因此客户端需要反复注册Watcher,即程序中在process里面又注册了Watcher
异步
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class ZK_Sample_Children_API_ASync implements Watcher{
private static CountDownLatch connCountDownLatch = new CountDownLatch(1);
private static ZooKeeper zookeeper = null;
private static String path = "/zk02";
@Override
public void process(WatchedEvent event) {
System.out.println("Receive watched event : " + event);
if(KeeperState.SyncConnected == event.getState()){
if(event.getType() == EventType.None && null == event.getPath()){
connCountDownLatch.countDown();
}else if(event.getType() == EventType.NodeChildrenChanged){
try {
System.out.println("child change reget childs : " + zookeeper.getChildren(path, true));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
zookeeper = new ZooKeeper("192.168.7.151:2181", 5000, new ZK_Sample_Children_API_ASync());
System.out.println(zookeeper.getState());
connCountDownLatch.await();
zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zookeeper.create(path + "/c1", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zookeeper.getChildren(path, true,new IChildren2Callback(),"获取子节点");
zookeeper.create(path + "/c2", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Thread.sleep(Integer.MAX_VALUE);
}
static class IChildren2Callback implements AsyncCallback.Children2Callback {
public void processResult(int rc, String path, Object ctx, List<String> children, Stat stat) {
System.out.println("Get Children znode result: [response code: " + rc + ", path: " + path + ", ctx: "
+ ctx + ", children list: " + children + ", stat: " + stat);
}
}
}
数据获取
Zookeeper API 提供了四个接口来获取数据,同样分同步,异步.
参数说明
更新数据
Zookeeper API 提供了两个接口来更新数据,同样分同步,异步.
参数说明
在更新数据时,setData方法存在一个version参数,其用于指定节点的数据版本,表明本次更新操作是针对指定的数据版本进行的,但是,在getData方法中,并没有提供根据指定数据版本来获取数据的接口,那么,这里为何要指定数据更新版本呢,这里方便理解,可以等效于CAS(compare and swap),对于值V,每次更新之前都会比较其值是否是预期值A,只有符合预期,才会将V原子化地更新到新值B。Zookeeper的setData接口中的version参数可以对应预期值,表明是针对哪个数据版本进行更新,假如一个客户端试图进行更新操作,它会携带上次获取到的version值进行更新,而如果这段时间内,Zookeeper服务器上该节点的数据已经被其他客户端更新,那么其数据版本也会相应更新,而客户端携带的version将无法匹配,无法更新成功,因此可以有效地避免分布式更新的并发问题。
检测节点是否存在
Zookeeper API 提供了四个接口来检测节点是否存在,同样分同步,异步.
参数说明
在调用接口时注册Watcher的话,还可以对节点是否存在进行监听,一旦节点被创建、被删除、数据更新,都会通知客户端
同步
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_Exist_API_Sync implements Watcher {
private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
private static ZooKeeper zk;
public static void main(String[] args) throws Exception {
String path = "/zk-book";
zk = new ZooKeeper("192.168.7.151:2181", 5000, //
new ZK_Sample_Exist_API_Sync());
connectedSemaphore.await();
zk.exists(path, true);
zk.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.setData(path, "123".getBytes(), -1);
zk.create(path + "/c1", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("success create znode: " + path + "/c1");
zk.delete(path + "/c1", -1);
zk.delete(path, -1);
Thread.sleep(Integer.MAX_VALUE);
}
public void process(WatchedEvent event) {
try {
if (KeeperState.SyncConnected == event.getState()) {
if (EventType.None == event.getType() && null == event.getPath()) {
connectedSemaphore.countDown();
} else if (EventType.NodeCreated == event.getType()) {
System.out.println("success create znode: " + event.getPath());
zk.exists(event.getPath(), true);
} else if (EventType.NodeDeleted == event.getType()) {
System.out.println("success delete znode: " + event.getPath());
zk.exists(event.getPath(), true);
} else if (EventType.NodeDataChanged == event.getType()) {
System.out.println("data changed of znode: " + event.getPath());
zk.exists(event.getPath(), true);
}
}
} catch (Exception e) {
}
}
}
输出结果
success create znode: /zk-book
data changed of znode: /zk-book
success create znode: /zk-book/c1
success delete znode: /zk-book
无论节点是否存在,都可以通过exists接口注册Watcher。
注册的Watcher,对节点创建、删除、数据更新事件进行监听。
对于指定节点的子节点的各种变化,不会通知客户端。
异步
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
public class ZK_Sample_Exist_API_ASync implements Watcher {
private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
private static ZooKeeper zk;
public static void main(String[] args) throws Exception {
String path = "/zk-book";
zk = new ZooKeeper("192.168.7.151:2181", 5000,
new ZK_Sample_Exist_API_ASync());
connectedSemaphore.await();
zk.exists(path, true, new IIStatCallback(), null);
zk.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
zk.setData(path, "123".getBytes(), -1);
zk.create(path + "/c1", "".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
System.out.println("success create znode: " + path + "/c1");
zk.delete(path + "/c1", -1);
zk.delete(path, -1);
Thread.sleep(Integer.MAX_VALUE);
}
public void process(WatchedEvent event) {
try {
if (KeeperState.SyncConnected == event.getState()) {
if (EventType.None == event.getType()
&& null == event.getPath()) {
connectedSemaphore.countDown();
} else if (EventType.NodeCreated == event.getType()) {
System.out.println("success create znode: "
+ event.getPath());
zk.exists(event.getPath(), true, new IIStatCallback(), null);
} else if (EventType.NodeDeleted == event.getType()) {
System.out.println("success delete znode: "
+ event.getPath());
zk.exists(event.getPath(), true, new IIStatCallback(), null);
} else if (EventType.NodeDataChanged == event.getType()) {
System.out.println("data changed of znode: "
+ event.getPath());
zk.exists(event.getPath(), true, new IIStatCallback(), null);
}
}
} catch (Exception e) {
}
}
static class IIStatCallback implements AsyncCallback.StatCallback {
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println("rc: " + rc + ", path: " + path + ", stat: " + stat);
}
}
}
输出结果
rc: -101, path: /zk-book, stat: null
success create znode: /zk-book
rc: 0, path: /zk-book, stat: 12884901993,12884901993,1508491896292,1508491896292,0,0,0,0,0,0,12884901993
data changed of znode: /zk-book
rc: 0, path: /zk-book, stat: 12884901993,12884901994,1508491896292,1508491896313,1,0,0,0,3,0,12884901993
success create znode: /zk-book/c1
success delete znode: /zk-book
rc: -101, path: /zk-book, stat: null
当节点不存在时,其rc(ResultCode)为-101。
权限认证
在实际过程中,可能是一个Zookeeper集群,有多个项目,多个服务共同使用,避免错误操作,数据读取权限,就需要进行认证了。
Zookeeper api 提供了权限认证
参数说明:
认证失败示例
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_ACL_API_AUTH_FAIL{
public static void main(String[] args) throws Exception {
String path = "/zk-book";
ZooKeeper zookeeper1 = new ZooKeeper("192.168.7.151:2181", 5000, null);
zookeeper1.addAuthInfo("digest", "foo:true".getBytes());
zookeeper1.create(path, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);
System.out.println("success create znode: " + path);
ZooKeeper zookeeper2 = new ZooKeeper("192.168.7.151:2181", 5000, null);
zookeeper2.getData(path, false, null);
}
}
输出结果
success create znode: /zk-book
Exception in thread "main" org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /zk-book
at org.apache.zookeeper.KeeperException.create(KeeperException.java:113)
at org.apache.zookeeper.KeeperException.create(KeeperException.java:51)
at org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1155)
at org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1184)
at com.jx.zk.first.ZK_Sample_ACL_API_AUTH_FAIL.main(ZK_Sample_ACL_API_AUTH_FAIL.java:16)
认证通过
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZK_Sample_ACL_API_AUTH_SUCC{
public static void main(String[] args) throws Exception {
String path = "/zk-book";
ZooKeeper zookeeper1 = new ZooKeeper("192.168.7.151:2181", 5000, null);
zookeeper1.addAuthInfo("digest", "foo:true".getBytes());
zookeeper1.create(path, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);
System.out.println("success create znode: " + path);
ZooKeeper zookeeper2 = new ZooKeeper("192.168.7.151:2181", 5000, null);
zookeeper2.addAuthInfo("digest", "foo:true".getBytes());
byte[] bt = zookeeper2.getData(path, false, null);
System.out.println(new String(bt));
}
}
输出结果
success create znode: /zk-book
init
不同的客户端之间带上一样的认证信息即可.