6.zookeeper watcher监听机制

watcher概念

zookeeper提供了数据的发布/订阅功能,多个订阅者可同时监听某一特定主题对象,当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等),会实时、主动通知所有订阅者。

zookeeper采用了Watcher机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户端压力。 watcher机制实际上与观察者模式类似,也可看作是一种观察者模式在分布式场景下的实现方式。

watcher架构

Watcher实现由三个部分组成:

  1. zookeeper服务端
  2. zookeeper客户端
  3. 客户端的ZKWatchManager对象

客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的WatchManager中。当zooKeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的WatchManager会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程。
在这里插入图片描述

watcher特性

特性说明
一次性watcher是一次性的,一旦被触发就会移除,再次使用时需要重新注册。
客户端顺序回调watcher回调是顺序串行化执行的,只有回调后客户端才能看到最新的数 据状态。一个watcher回调逻辑不应该太多,以免影响别的watcher执行。
轻量级WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,但不会告诉数据节点变化前后的具体内容。
时效性watcher只有在当前session彻底失效时才会无效,若在session有效期内 快速重连成功,则watcher依然存在,仍可接收到通知。

watcher接口设计

Watcher是一个接口,任何实现了Watcher接口的类就是一个新的Watcher。 Watcher内部包含了三个枚举类:KeeperState、EventType、WatcherType。在这里插入图片描述

Watcher通知状态(KeeperState)

KeeperState是客户端与服务端连接状态发生变化时对应的通知类型。是一个枚举类,其枚举属性如下:

枚举属性说明
Disconnected客户端与服务器断开连接时。
SyncConnected客户端与服务器正常连接时(异步)。
NoSyncConnected已过时
AuthFailed客户端身份认证失败时
ConnectedReadOnly客户端以只读的方式连接服务器时。
SaslAuthenticated客户端用SASL权限验证时。
Expired客户端会话session失效时。
Closed客户端与服务端连接关闭时。
Unknown已过时
Watcher事件类型(EventType)

EventType是数据节点(znode)发生变化时对应的通知类型。EventType变化时KeeperState永远处于SyncConnected通知状态下。当KeeperState发生变化时,EventType永远为None。EventType是一个枚举类,枚举属性如下:

枚举属性说明
None无。
NodeCreatedWatcher监听的数据节点被创建时。
NodeDeletedWatcher监听的数据节点被删除时。
NodeDataChangedWatcher监听的数据节点内容发生变更时(无论内容数据 是否变化)。
NodeChildrenChangedWatcher监听的数据节点的子节点列表发生变更时。
DataWatchRemoved客户端用SASL权限验证时。
ChildWatchRemoved客户端会话session失效时。
PersistentWatchRemoved客户端与服务端连接关闭时。

注册事件方式

在zookeeper中采用exists(path, watch)、getData(path, watcher, stat) 、getChildren(path, watch)这样的方式为某个znode注册监听。

注册方式CreatedChildrenChangedChangedDeleted
exists可监控可监控可监控
getData可监控可监控
getChildren可监控可监控

用法

监听连接状态
package com.huazai.zookeeper.zkexample.config;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.util.concurrent.CountDownLatch;

/**
 * @author pyh
 * @date 2021/5/19 0:20
 */
public class ZookeeperConnection {
    public static void main(String[] args) {
        connect();
    }

    public static ZooKeeper zooKeeper;

    public static ZooKeeper connect() {
        try {
            // 由于连接zookeeper服务器是异步连接,需要CountDownLatch阻塞主线程,等待子线程连接结果后反馈给主线程
            CountDownLatch countDownLatch = new CountDownLatch(1);
            /*
                connectString:服务器的ip和端口
                sessionTimeout:客户端与服务器之间的会话超时时间,以毫秒为单位的
                watcher:监视器对象
            */
            zooKeeper = new ZooKeeper("192.168.64.129:2181", 5000, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                        System.out.println("zookeeper异步连接成功");
                    } else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
                        System.out.println("断开连接");
                    } else if (watchedEvent.getState() == Event.KeeperState.Expired) {
                        System.out.println("会话超时");
                    } else if (watchedEvent.getState() == Event.KeeperState.AuthFailed) {
                        System.out.println("认证失败");
                    } else if (watchedEvent.getState() == Event.KeeperState.Closed) {
                        System.out.println("连接关闭");
                    }
                    countDownLatch.countDown();
                }
            });
            // 主线程阻塞等待连接对象的创建成功
            countDownLatch.await();
            // 会话编号
            System.out.println("客户端sessionId:" + zooKeeper.getSessionId());
            return zooKeeper;
        } catch (Exception e) {
            System.out.println("zookeeper连接异常");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭zookeeper会话
     */
    public static void close() {
        if (zooKeeper != null) {
            try {
                zooKeeper.close();
                System.out.println("zookeeper关闭成功");
            } catch (InterruptedException e) {
                System.out.println("zookeeper关闭失败");
                e.printStackTrace();
            }
        }
    }
}
监听节点是否存在
public Stat exists(String path, boolean watch);
public Stat exists(final String path, Watcher watcher);
  • path:znode路径。
  • watch:是否使用连接对象中注册的监视器。
  • watcher:监视器对象。
package com.huazai.zookeeper.zkexample.watcher;

import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @author pyh
 * @date 2021/6/1 0:05
 */
public class ExistsWatch {
    private ZooKeeper zooKeeper;

    @Before
    public void before() {
        zooKeeper = ZookeeperConnection.connect();
    }

    @Test
    public void watchExists() throws Exception {
        /*
            watch为true,则默认使用ZooKeeper构造函数的监听器
         */
        zooKeeper.exists("/watcher1", true);
        Thread.sleep(50000);
        System.out.println("结束");
    }

    @Test
    public void watchExists1() throws Exception {
        zooKeeper.exists("/watcher1", 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("结束");
    }

    /**
     * 由于监听器一次性特性,监听器一旦触发就会被移除,再次使用时需要重新注册。
     * 可以在触发监听回调的时候紧接着注册监听器,由此伪装性的达到一次监听多次触发的效果
     *
     * @throws Exception
     */
    @Test
    public void watchExists2() throws Exception {
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("自定义watcher");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                    // 监听回调中再次注册监听器,以此反复,伪装性的达到一次监听多次触发的效果
                    zooKeeper.exists("/watcher1", this);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        // 第一次监听
        zooKeeper.exists("/watcher1", watcher);
        Thread.sleep(500000);
        System.out.println("结束");
    }

    /**
     * 一个节点可以注册多个监听器,由于监听器特性可知,多个监听器执行是顺序串行回调的
     *
     * @throws Exception
     */
    @Test
    public void watchExists3() throws Exception {
        for (int i = 0; i < 10; i++) {
            zooKeeper.exists("/watcher1", 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("结束");
    }

    @After
    public void after() {
        ZookeeperConnection.close();
    }
}
监听节点数据变化
public byte[] getData(String path, boolean watch, Stat stat);
public byte[] getData(final String path, Watcher watcher, Stat stat);
  • path:znode路径。
  • watch:是否使用连接对象中注册的监视器。
  • watcher:监视器对象。
  • stat:返回znode的元数据。
package com.huazai.zookeeper.zkexample.watcher;

import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @author pyh
 * @date 2021/6/1 0:42
 */
public class GetDataWatch {
    private ZooKeeper zooKeeper;

    @Before
    public void before() {
        zooKeeper = ZookeeperConnection.connect();
    }

    @Test
    public void watchGetData() throws Exception {
        /*
            watch为true,则默认使用ZooKeeper构造函数的监听器
         */
        Stat stat = new Stat();
        zooKeeper.getData("/watcher1", true, stat);
        System.out.println(stat);
        Thread.sleep(50000);
        System.out.println("结束");
    }

    @Test
    public void watchGetData1() throws Exception {
        Stat stat = new Stat();
        zooKeeper.getData("/watcher1", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("自定义watcher");
                System.out.println("path=" + event.getPath());
                System.out.println("eventType=" + event.getType());
            }
        }, stat);
        Thread.sleep(50000);
        System.out.println("结束");
    }

    /**
     * 由于监听器一次性特性,监听器一旦触发就会被移除,再次使用时需要重新注册。
     * 可以在触发监听回调的时候紧接着注册监听器,由此伪装性的达到一次监听多次触发的效果
     *
     * @throws Exception
     */
    @Test
    public void watchGetData2() throws Exception {
        Stat stat = new Stat();
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("自定义watcher");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                    // 监听回调中再次注册监听器,以此反复,伪装性的达到一次监听多次触发的效果
                    zooKeeper.getData("/watcher1", this, stat);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        // 第一次监听
        zooKeeper.getData("/watcher1", watcher, stat);
        Thread.sleep(500000);
        System.out.println("结束");
    }

    /**
     * 一个节点可以注册多个监听器,由于监听器特性可知,多个监听器执行是顺序串行回调的
     *
     * @throws Exception
     */
    @Test
    public void watchGetData3() throws Exception {
        Stat stat = new Stat();
        for (int i = 0; i < 10; i++) {
            zooKeeper.getData("/watcher1", new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    System.out.println("自定义watcher");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                }
            }, stat);
        }
        Thread.sleep(50000);
        System.out.println("结束");
    }

    @After
    public void after() {
        ZookeeperConnection.close();
    }
}
监听子节点列表
public List<String> getChildren(String path, boolean watch);
public List<String> getChildren(final String path, Watcher watcher);
  • path:znode路径。
  • watch:是否使用连接对象中注册的监视器。
  • watcher:监视器对象。
package com.huazai.zookeeper.zkexample.watcher;

import com.huazai.zookeeper.zkexample.config.ZookeeperConnection;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @author pyh
 * @date 2021/6/2 22:22
 */
public class GetChildrenWatch {
    private ZooKeeper zooKeeper;

    @Before
    public void before() {
        zooKeeper = ZookeeperConnection.connect();
    }

    @Test
    public void watchGetChildren() throws Exception {
        /*
            watch为true,则默认使用ZooKeeper构造函数的监听器
         */
        zooKeeper.getChildren("/watcher1", true);
        Thread.sleep(50000);
        System.out.println("结束");
    }

    @Test
    public void watchGetChildren1() throws Exception {
        zooKeeper.getChildren("/watcher1", 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("结束");
    }

    /**
     * 由于监听器一次性特性,监听器一旦触发就会被移除,再次使用时需要重新注册。
     * 可以在触发监听回调的时候紧接着注册监听器,由此伪装性的达到一次监听多次触发的效果
     *
     * @throws Exception
     */
    @Test
    public void watchGetChildren2() throws Exception {
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    System.out.println("自定义watcher");
                    System.out.println("path=" + event.getPath());
                    System.out.println("eventType=" + event.getType());
                    // 监听回调中再次注册监听器,以此反复,伪装性的达到一次监听多次触发的效果
                    zooKeeper.getChildren("/watcher1", this);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        // 第一次监听
        zooKeeper.getChildren("/watcher1", watcher);
        Thread.sleep(500000);
        System.out.println("结束");
    }

    /**
     * 一个节点可以注册多个监听器,由于监听器特性可知,多个监听器执行是顺序串行回调的
     *
     * @throws Exception
     */
    @Test
    public void watchGetChildren3() throws Exception {
        for (int i = 0; i < 10; i++) {
            zooKeeper.getChildren("/watcher1", 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("结束");
    }

    @After
    public void after() {
        ZookeeperConnection.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NPException.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值