Zookeeper(四)

102 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

6.节点监听机制 watch

客户端可以监测znode节点的变化。Zonode节点的变化触发相应的事件,然后清除对该节点的监测。当监测一个znode节点时候,Zookeeper会发送通知给监测节点。**一个Watch事件是一个一次性的触发器,当被设置了Watch的数据和目录发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端以便通知它们。**监听器是一次性触发器,只要触发过一次,下一次就失效了。

# 1.ls /path true             监听节点目录的变化
	例:ls /node1 true
# 2.get /path true						监听节点数据的变化。   
	例:get /node1 true

7.java操作ZK

7.1 创建项目引入依赖

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>

7.2 获取zk客户端对象

private ZkClient zkClient;

// 获取zk客户端连接
@Before
public void Before(){
  //参数1:服务器的ip和端口
  //参数2:会话的超时时间
  //参数3:回话的连接时间
  //参数4:序列化方式
  zkClient = new ZkClient("192.168.28.132:2181",30000,60000,new SerializableSerializer());
}

// 释放资源
@After
public void after(){
    zkClient.close();
}

7.3 常用API

  • 创建节点
// 创建结点
@Test
public void testCreateNode(){
    //第一种创建方式  返回创建节点的名称
    String nodeName = zkClient.create("/node5","lisi", CreateMode.PERSISTENT);
    zkClient.create("/node6","zhangsan", CreateMode.PERSISTENT_SEQUENTIAL);
    zkClient.create("/node7","王五",CreateMode.EPHEMERAL);
    zkClient.create("/node8","xiaozhang",CreateMode.EPHEMERAL_SEQUENTIAL);
    //第二种创建方式 不会返回创建节点的名称
    zkClient.createPersistent("/node1","持久数据");
    zkClient.createPersistentSequential("/node1/aa","持久数据顺序节点");
    zkClient.createEphemeral("/node2","临时节点");
    zkClient.createEphemeralSequential("/node1/bb","临时顺序节点");
}
  • 删除节点
// 删除节点
@Test
public void testDeleteNode(){
    //删除没有子节点的节点  返回值:是否删除成功
    boolean delete = zkClient.delete("/node1");
    //递归删除节点信息 返回值:是否删除成功
    boolean recursive = zkClient.deleteRecursive("/node1");
}
  • 查看节点的子节点
// 查询结点
@Test
public void testFindNodes(){
    //获取指定路径的节点信息  //返回值: 为当前节点的子节点信息
    List<String> children = zkClient.getChildren("/");
    for (String child : children) {
        System.out.println(child);
    }
}
  • 查看当前节点的数据
    • 注意:如果出现:org.I0Itec.zkclient.exception.ZkMarshallingError: java.io.StreamCorruptedException: invalid stream header: 61616161 异常的原因是: 在shell中的数据序列化方式 和 java代码中使用的序列化方式不一致导致 因此要解决这个问题只需要保证序列化一致即可 都使用相同端操作即可
// 获取节点的数据
@Test
public void testFindNodeData(){
    Object readData = zkClient.readData("/node1");
    System.out.println(readData);
}
  • 查看当前节点的数据并获取状态信息
// 获取数据以及当前节点的状态信息
@Test
public void testFindNodeDataAndStat(){
  Stat stat = new Stat();
  Object readData = zkClient.readData("/node1",stat);
  System.out.println(readData);
  System.out.println(stat.getCversion());     // 状态版本
  System.out.println(stat.getCtime());        // 创建时间
  System.out.println(stat.getCzxid());        // 创建id
}
  • 修改节点数据
// 修改节点数据
@Test
public void testUpdateNodeData(){
  User user = new User(1, "小陈", 23, new Date());  // 注:这个对象需要实现Serializable接口
  zkClient.writeData("/node1", user);
}
  • 监听节点数据的变化 (注:使用java客户端监听的话要求数据修改是通过java修改才可以监听到)

java中监听是永久监听

// 监听节点数据的变化
@Test
public  void testWatchDataChange() throws IOException {
  zkClient.subscribeDataChanges("/node1", new IZkDataListener() {
    // 当前节点数据变化时触发这个方法
    public void handleDataChange(String dataPath, Object data) throws Exception {
      System.out.println("当前节点路径:" + dataPath);
      System.out.println("当前节点变化后数据:" + data);
    }
    // 当前节点删除时触发这个方法
    public void handleDataDeleted(String dataPath) throws Exception {
      System.out.println("当前节点路径:" + dataPath);
    }
  });
  //阻塞客户端去监听
  System.in.read();
}
  • 监听节点目录的变化(注:使用java客户端监听的话要求目录修改是通过java修改才可以监听到)

java中监听是永久监听

// 监听节点目录的变化
@Test
public  void testOnNodesChange() throws IOException {
  zkClient.subscribeChildChanges("/node1", new IZkChildListener() {
    //当节点的目录发生变化时,会自动调用这个方法
    //参数1:父节点名称
    //参数2:父节点中的所有子节点名称
    public void handleChildChange(String nodeName, List<String> list) throws Exception {
      System.out.println("父节点名称: " + nodeName);
      System.out.println("发生变更后所有子节点名称:");
      for (String name : list) {
        System.out.println(name);
      }
    }
  });
  //阻塞java客户端去监听
  System.in.read();
}

总测试代码:

public class TestZkClient {

    private ZkClient zkClient;

    // 1. 在zk中创建结点
    @Test
    public void testCreateNode(){
        // 1. 持久结点  参数:创建的路径 数据 结点的类型
        zkClient.create("/node1", "xiaochen", CreateMode.PERSISTENT);
        // 2. 持久顺序结点
        zkClient.create("/node1/names", "zhangsan", CreateMode.PERSISTENT_SEQUENTIAL);
        // 3. 临时结点
        zkClient.create("/node1/lists", "xiaoxiao", CreateMode.EPHEMERAL);
        // 4. 临时顺序结点
        zkClient.create("/node1/lists11", "xiaoming", CreateMode.EPHEMERAL_SEQUENTIAL);
    }

    // 2. 删除节点
    @Test
    public void testDeleteNode(){
        //删除没有子节点的节点  返回值:是否删除成功
        boolean delete = zkClient.delete("/node1");
        //递归删除节点信息 返回值:是否删除成功
        boolean recursive = zkClient.deleteRecursive("/node1");
    }

    // 3. 查询当前节点的所有子节点
    @Test
    public void testFindNodes(){
        //获取指定路径的节点信息  //返回值: 为当前节点的子节点信息
        List<String> children = zkClient.getChildren("/");
        for (String child : children) {
            System.out.println(child);
        }
    }

    // 4. 获取指定节点的数据  注意:通过java客户端操作需要保证节点存储的数据和获取节点时数据序列化方式必须一致
    //                             如果在zk那边手动创建,序列化方式为字符串那种序列化,在java中是对象序列化方式
    //                             所以我们一定要保证获取指定节点的数据时该节点是通过java客户端创建的,这样序列化方式一致
    @Test
    public void testFindNodeData(){
        Object readData = zkClient.readData("/node1");//一定要保证该节点创建和获取时序列化方式相同(通过java客户端创建和获取)
        System.out.println(readData);
    }

    // 5. 获取指定节点数据以及节点的状态信息
    @Test
    public void testFindNodeDataAndStat(){
        Stat stat = new Stat();
        Object readData = zkClient.readData("/node1",stat);
        System.out.println(readData);
        System.out.println(stat.getCversion());     // 状态版本
        System.out.println(stat.getCtime());        // 创建时间
        System.out.println(stat.getCzxid());        // 创建id
    }

    // 6. 修改节点数据
    @Test
    public void testUpdateNodeData(){
        User user = new User(1, "小陈", 23, new Date());  // 注:这个对象需要实现Serializable接口
        zkClient.writeData("/node1", user);
        User o = zkClient.readData("/node1");
        System.out.println(o);
    }

    // 7. 监听节点的数据变化
    @Test
    public  void testWatchDataChange() throws IOException {
        zkClient.subscribeDataChanges("/node1", new IZkDataListener() {
            // 当前节点数据变化时触发这个方法
            public void handleDataChange(String dataPath, Object data) throws Exception {
                System.out.println("当前节点路径:" + dataPath);
                System.out.println("当前节点变化后数据:" + data);
            }
            // 当前节点删除时触发这个方法
            public void handleDataDeleted(String dataPath) throws Exception {
                System.out.println("当前节点路径:" + dataPath);
            }
        });
        //阻塞客户端
        System.in.read();
    }

    // 8. 监听节点目录的变化
    @Test
    public  void testOnNodesChange() throws IOException {
        zkClient.subscribeChildChanges("/node1", new IZkChildListener() {
            //当节点的目录发生变化时,会自动调用这个方法
            //参数1:父节点名称
            //参数2:父节点中的所有子节点名称
            public void handleChildChange(String nodeName, List<String> list) throws Exception {
                System.out.println("父节点名称: " + nodeName);
                System.out.println("发生变更后所有子节点名称:");
                for (String name : list) {
                    System.out.println(name);
                }
            }
        });
        //阻塞java客户端去监听
        System.in.read();
    }

    // 初始化客户端对象,获取连接
    @Before
    public void before(){
        // 获取连接   参数:zk服务器ip及端口  会话超时时间(单位毫秒)  连接超时时间(单位毫秒)  序列化方式(存储对象时需要序列化)
        zkClient = new ZkClient("192.168.204.132:2181", 60000*30, 60000, new SerializableSerializer());
    }

    // 获取连接
    public static void main(String[] args) {
        System.out.println();
    }

    // 释放资源
    @After
    public void after(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        zkClient.close();
    }
}