1.zookeeper的事件监听机制
zookeeper提供了数据的发布订阅功能,监听者通过注册监听器,监听不同的节点,当节点的数据发生变化时,会通知监听者
zookeeper的监听机制采用异步回调的方式,客户端不用一直轮询状态的变化,减少了客户端的压力
(一)watcher的架构
zookeeper的监听器的组成:
- zookeeper客户端
- zookeeper服务端
- zookeeper客户端的ZKWatcherManager对象
(二)监听原理
(1)客户端将watcher注册到服务端
(2)客户端将watcher对象同时注册到客户端的ZKWatcherManager对象中
(3)Zookeeper服务端监听到数据发生变化,服务端会主动通知客户端,客户端的ZKWatcherManager对象主动调用对应的watcher进行处理
(三)Zookeeper监听器的特点
(1)一次性:watcher是一次性的,一旦被触发就会被移除,再次使用时需要重新进行注册
(2)客户端循序回调:watcher的回调是顺序执行的,只有回调后客户端才能看见最新的数据状态,回调函处理逻辑不应该过于复杂,以免影响其他watcher逻辑的执行
(3)轻量级:WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容;
(4)时效性: watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知;
(四)watcher的通知状态(KeeperState)
keeperstate描述的是客户端与服务器端的链接状态,状态的类型是一个枚举类,位于org.apache.zookeeper.Watcher.Event.KeeperState下,枚举类的属性如下:
| 枚举属性 | 说明 |
|---|---|
| SyncConnected | 客户端与服务器正常连接时 |
| Disconnected | 客户端与服务器断开连接时 |
| Expired | 会话session失效时 |
| AuthFailed | 身份认证失败时 |
(五)watcher的事件类型(EventType)
EventType描述的是数据节点发生变化时对应的通知类型,只有当KeeperState为SyncConnected状态下时EventType才有效,当keeperstate发生变化时,EventType永远为null,EventType的状态类型也是一个枚举类,位于org.apache.zookeeper.Watcher.Event.EventType包下,枚举类的属性如下:
| 枚举属性 | 说明 |
|---|---|
| None | 无 |
| NodeCreated | Watcher监听的数据节点被创建时 |
| NodeDeleted | Watcher监听的数据节点被删除时 |
| NodeDataChanged | Watcher监听的数据节点内容发生变更时(无论内容数据是否变化) |
| NodeChildrenChanged | Watcher监听的数据节点的子节点列表发生变更时 |
(六)注册监听实现的方式
zookeeper中采用zk.getChildren(path, watch)、zk.exists(path, watch)、zk.getData(path, watcher, stat)这样的方式为某个znode注册监听。
下表以node-x节点为例,说明调用的注册方法和可监听事件间的关系:
| 注册方式 | Created | ChildrenChanged | Changed | Deleted |
|---|---|---|---|---|
| zk.exists(“/node-x”,watcher) | 可监控 | 可监控 | 可监控 | |
| zk.getData(“/node-x”,watcher) | 可监控 | 可监控 | ||
| zk.getChildren(“/node-x”,watcher) | 可监控 | 可监控 |
注意:客户端接收到的相关事件通知中只包含状态及类型等信息,不包括节点变化前后的 具体内容,变化前的数据需业务自身存储,变化后的数据需调用get等方法重新获取;
2.Zookeeper监听事件的代码实现
(一)查看链接状态
在new Zookeeper对象时,传入watcher对象,可以监控链接状态
event.getType()
public class c08监控查看链接状态 implements Watcher{
static String ip = "mymv:2181";
// 计数器对象
static CountDownLatch countDownLatch = new CountDownLatch(1);
// 连接对象
static ZooKeeper zooKeeper;
@Override
public void process(WatchedEvent event) {
//得到通知类型,当链接状态变化时,事件类型为none
if (event.getType() == Event.EventType.None) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("链接创建成功");
countDownLatch.countDown();
}else if (event.getState() == Event.KeeperState.Disconnected) {
System.out.println("断开链接");
}else if (event.getState() == Event.KeeperState.Expired) {
System.out.println("会话超时");
try {
zooKeeper = new ZooKeeper(ip, 5000, new c08监控查看链接状态());
} catch (IOException e) {
e.printStackTrace();
}
}else if (event.getState() == Event.KeeperState.AuthFailed) {
System.out.println("认证失败");
}
}
}
public static void main(String[] args) {
try {
zooKeeper = new ZooKeeper(ip, 5000, new c08监控查看链接状态());
// 阻塞线程等待连接的创建
countDownLatch.await();
// 会话id
System.out.println(zooKeeper.getSessionId());
// 添加授权用户
zooKeeper.addAuthInfo("digest","author:123456".getBytes());
//添加监控节点,是否使用连接对象中注册的监视器。,Stat对象
byte [] bs=zooKeeper.getData("/create/nodeCode",false,null);
System.out.println(new String(bs));
Thread.sleep(50000);
zooKeeper.close();
System.out.println("结束");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
(二)exists()监控节点是否存在
// 使用连接对象的监视器
exists(String path, boolean b)
// 自定义监视器
exists(String path, Watcher w)
public class c09监控查看节点是否存在 {
String ip = "mymv:2181";
ZooKeeper zooKeeper = null;
@Before
public void before() throws IOException, InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
// 连接zookeeper客户端
zooKeeper = new ZooKeeper(ip, 6000, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("连接对象的参数!");
// 连接成功
if (event.getState() == Event.KeeperState.SyncConnected)
{
countDownLatch.countDown();
}
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
});
countDownLatch.await();
}
@After
public void after() throws InterruptedException {
zooKeeper.close();
}
@Test
public void watcherExists2() throws KeeperException, InterruptedException {
zooKeeper.exists("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
});
Thread.sleep(50000);
System.out.println("结束");
}
@Test
public void watcherExists3() throws KeeperException, InterruptedException {
zooKeeper.exists("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
});
zooKeeper.exists("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
});
Thread.sleep(50000);
System.out.println("结束");
}
}
(三)gerData()查看节点注册监控器
// 使用连接对象的监视器
getData(String path, boolean b, Stat stat)
// 自定义监视器
getData(String path, Watcher w, Stat stat)
public class c10监控查看节点 {
String ip = "mymv:2181";
ZooKeeper zooKeeper = null;
@Before
public void before() throws IOException, InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
// 连接zookeeper客户端
zooKeeper = new ZooKeeper(ip, 6000, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("连接对象的参数!");
// 连接成功
if (event.getState() == Event.KeeperState.SyncConnected)
{
countDownLatch.countDown();
}
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
});
countDownLatch.await();
}
@After
public void after() throws InterruptedException {
zooKeeper.close();
}
@Test
public void watcherGet1() throws KeeperException, InterruptedException {
zooKeeper.getData("/create/node3",false,new Stat());
Thread.sleep(50000);
System.out.println("结束");
}
@Test
public void watcherGet2() throws KeeperException, InterruptedException {
zooKeeper.getData("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
},null);
Thread.sleep(50000);
System.out.println("结束");
}
@Test
public void watcherGet3() throws KeeperException, InterruptedException {
zooKeeper.getData("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
},null);
zooKeeper.getData("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
},null);
Thread.sleep(50000);
System.out.println("结束");
}
}
(四)getChildren()查看子节点注册监控器
// 使用连接对象的监视器
getChildren(String path, boolean b)
// 自定义监视器
getChildren(String path, Watcher w)
public class c11监控查看子节点 {
String ip = "mymv:2181";
ZooKeeper zooKeeper = null;
@Before
public void before() throws IOException, InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
// 连接zookeeper客户端
zooKeeper = new ZooKeeper(ip, 6000, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("连接对象的参数!");
// 连接成功
if (event.getState() == Event.KeeperState.SyncConnected)
{
countDownLatch.countDown();
}
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
});
countDownLatch.await();
}
@After
public void after() throws InterruptedException {
zooKeeper.close();
}
@Test
public void watcherChildren1() throws KeeperException, InterruptedException {
zooKeeper.getChildren("/create/node3",true);
Thread.sleep(50000);
System.out.println("结束");
}
@Test
public void watcherChildren12() throws KeeperException, InterruptedException {
zooKeeper.getChildren("/create/node3", new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("自定义watcher");
System.out.println("path=" + event.getPath());
System.out.println("eventType=" + event.getType());
}
},null);
Thread.sleep(50000);
System.out.println("结束");
}
}