目录:
一、zookeeper节点类型:
PERSISTENT:持久化节点
PERSISTENT_SEQUENTIAL:持久化顺序节点
EPHEMERAL:临时节点
EPHEMERAL_SEQUENTIAL:临时顺序节点
持久化节点:一经创建,除非客户端手动删除,否则永久存在,持久化节点可以继续创建子节点。
持久化顺序节点:在持久化节点的基础上,zookeeper会自动为顺序节点后添加一个递增的序号。
临时节点:生命周期和客户端会话Session相同,且临时节点不能创建子节点,客户端连接断开后,服务器立即删除该节点或保留临时节点一小段时间,由客户端指定的sessionTimeout决定,目的是防止客户端由于网络波动导致临时节点被删除,通常是2~4s。
临时顺序节点:在临时节点的基础上,为每个节点添加一个递增的序号
这时候/lock目录下的节点如下,可以看到有3个持久化顺序节点和一个临时节点,还有两个临时顺序节点。
这时候我们运行:quit命令退出客户端。
再重新连接客户端:
zkCli.sh -server localhost:2181
再查看 /lock 节点下的子节点:
可以看到,只剩下3个持久化顺序节点,而另外3个临时节点已经自动删除。
二、Zookeeper的Watcher监听:
- 监听某个节点的变化。
- 一个监听器只会被触发一次。
- 一个节点可以注册多个Watcher监听,当这个节点发生事件时,会依次触发所有监听该节点的监听器。
- 同一个Watcher实例,对同一个节点,只能注册一次监听,注册多次只会触发一次,而不会触发多次。
- 每个Watcher监听都有一个核心方法:
//该方法在收到回调事件的时候会执行
public void process(WatchedEvent event){
//节点路径
String path = event.getPath();
//事件类型
Event.EventType eventType = event.getType();
//客户端状态
Event.KeeperState eventState = event.getState();
}
其中方法的参数:(WatchedEvent event)作为回调方法的参数,在回调方法执行时被传入,其中最核心的三个属性分别为:
-
path:监听的节点路径
-
Event.EventType:Watcher触发的事件类型
Event.EventType 描述 None 客户端连接状态发生改变,也就是Event.KeeperState发生了变化 NodeCreated 节点创建了 NodeDeleted 节点被删除了 NodeDataChanged 节点数据发生改变了 NodeChildrenChanged 节点的子节点发生改变了 -
Event.KeeperState:客户端状态
Event.KeeperState 描述 Disconnected 客户端断开连接 SyncConnected 客户端连接成功 AuthFailed 客户端身份验证失败 ConnectedReadOnly 客户端以只读的方式连接 SaslAuthenticated 客户端身份验证通过 Expired 客户端过期
三、怎么设置Watcher监听:
1.可以通过客户端的getData()、exists()、getChildren()方法设置。
//判断节点是否存在并注册一个Watcher监听
@Test
void exist2() throws InterruptedException, KeeperException {
//判断节点是否存在,并注册监听事件,注意:一个监听事件只能触发一次,如果需要多次触发
//需要第一个监听事件触发后再注册一个监听事件
Stat exists = zkClient.exists("/lock/e-node", new Watcher() {
//如果该节点发生变化,比如创建,或者删除时,就会回调该方法
@Override
public void process(WatchedEvent event) {
//节点路径
String path = event.getPath();
//事件类型
Event.EventType eventType = event.getType();
//客户端状态
Event.KeeperState eventState = event.getState();
switch (eventType) {
case None:
System.out.println("客户端连接状态发生改变");
break;
case NodeCreated:
System.out.println(path+"节点创建了");
break;
case NodeDeleted:
System.out.println(path+"节点删除了");
break;
case NodeDataChanged:
System.out.println(path+"节点数据发生改变了");
break;
case NodeChildrenChanged:
System.out.println(path+"节点的子节点发生改变了");
break;
}
switch (eventState) {
case Disconnected:
System.out.println("客户端断开连接");
break;
case SyncConnected:
System.out.println("客户端连接成功");
break;
case AuthFailed:
System.out.println("客户端身份验证失败");
break;
case ConnectedReadOnly:
System.out.println("客户端以只读连接");
break;
case SaslAuthenticated:
System.out.println("客户端身份验证通过");
break;
case Expired:
System.out.println("客户端过期");
break;
}
}
});
//程序休眠一段时间,否则程序结束后就收不到回调信息
Thread.sleep(300000);
}
四、Zookeeper异步API调用:AsyncCallback
zookeeper客户端的api分为两种:一种是同步,一种是异步
同步API:很好理解,调用同步api后,主线程处于阻塞状态,等待zk返回结果后,客户端才能继续执行。
异步API:跟同步API相反,客户端调用完后,不需要等待zk返回结果,客户端继续执行后面的代码,但是需要设置一个AsyncCallback回调,当zk服务器返回结果后,会回调客户端注册的这个AsyncCallback中的processResult()方法。来处理返回结果。
AsyncCallback类型:
类型 | 描述 | 使用场景/对应异步api |
---|---|---|
StatCallback | 用于获取节点的状态 | exists()、setData() |
DataCallback | 用于获取节点的数据和状态 | getData()、 |
ACLCallback | 用于获取节点的ACL和状态 | getACL() |
ChildrenCallback | 用于获取节点的子节点 | getChildren() |
Children2Callback | 用于获取节点的子节点和状态 | getChildren() |
StringCallback | 只返回节点的名称 | create() |
VoidCallback | 空回调,不会返回任何数据 | delete()、sync() |
MultiCallback | 用于处理有多个结果的回调 | multi()、multiInternal() |
例子:
//获取节点数据:同步
@Test
void getData() {
String data = zkApi.getData("/node1", new WatcherApi());
System.out.println("/test1=="+data);
}
//获取节点数据:异步
@Test
void getAsyncData() throws InterruptedException {
zkClient.getData("/node2", false, new AsyncCallback.DataCallback() {
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
System.out.println("异步回调方法获取的数据:"+path+":"+data);
}
},111);
//主线程休眠,等待回调方法执行
Thread.sleep(30000);
}
//判断节点是否存在:同步
@Test
void exist0() throws InterruptedException, KeeperException {
//调用同步api
Stat stat = zkClient.exists("/node3", false);
System.out.println("节点状态:"+stat);
}
//判断节点是否存在:异步
@Test
void exist() throws InterruptedException {
//监听器
MyWatcher myWatcher = new MyWatcher();
//调用异步api
zkClient.exists("/node4", false, new AsyncCallback.StatCallback() {
//调如果zookeeper服务器返回结果,会回调该方法
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println("节点状态stat:"+stat);
}
},"上下文");
System.out.println("主线程等待zk服务器返回结果");
//主线程阻塞,等待回调
Thread.sleep(600000);
}
五、Watcher和AsyncCallback的区别:
1.都是异步回调的方式。
2.Watcher关注的是节点的状态变化,AsyncCallback关注的是API调用返回结果,一个是监听节点将来的变化,一个是获取节点当前的状态或数据,所以两者并不冲突,也可以共存。
//watcher和AsyncCallback共存
@Test
public void watcherAndCallback() throws InterruptedException {
//使用异步api判断节点是否存在,并注册一个Watcher监听
zkClient.exists("/test-node", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getPath() + "节点事件:" + event.getType());
}
}, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
System.out.println(path + "节点状态:" + stat);
}
}, "上下文");
//主线程休眠1分钟等待回调
Thread.sleep(60000);
}
执行结果:
/test-node节点状态:null
/test-node节点事件:NodeCreated
在这个方法中调用了异步API获取节点是否存在,程序运行后,AsyncCallback立马收到了服务端的返回结果null,说明当前节点不存在,紧接着立马通过linux下启动的zookeeper客户端create /test-node 111创建该节点,然后Watcher监听器立马监听到了该节点的创建事件。
本文中的zkClient来自spring自动注入
@Autowired
ZooKeeper zkClient;
@Bean(name = "zkClient")
public ZooKeeper zkClient(){
ZooKeeper zooKeeper=null;
try {
final CountDownLatch countDownLatch = new CountDownLatch(1);
//连接成功后,会回调Watcher中的process()方法,所以先通过CountDownLatch()让线程处于阻塞状态
// ,等待服务器的异步回调
zooKeeper = new ZooKeeper(address, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(Event.KeeperState.SyncConnected==event.getState()){
//如果服务器返回"已连接"状态,说明客户端连接成功
countDownLatch.countDown();
}
}
});
countDownLatch.await();
logger.info("ZooKeeper初始化状态:{}",zooKeeper.getState());
}catch (Exception e){
logger.error("ZooKeeper连接异常:{}",e);
}
return zooKeeper;
}
参考:https://blog.csdn.net/u010391342/article/details/100404588
…END…
推荐文章:
Zookeeper小白必看
教你最快速搭建你的Zookeeper集群
如果觉得文章对你有帮助的话,可以微信关注我的公众号:码农小诚
关注我的公众号更多技术文章分享实时更新,与你共同成长,回复【555】还有55本java技术书籍,免费送,回复【zk】还能获取更多zookeeper相关技术书籍。