Spring整合ZooKeeper基础使用介绍

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,广泛应用于分布式系统中,比如有用它做配置中心,注册中心,也有使用它来实现分布式锁的,作为高并发技术栈中不可或缺的一个基础组件,接下来我们将看一下,zk应该怎么玩,可以怎么玩本文作为第一篇,将主要介绍基于zk-client的基本使用姿势,依次来了解下zk的基本概念

image.png


ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,广泛应用于分布式系统中,比如有用它做配置中心,注册中心,也有使用它来实现分布式锁的,作为高并发技术栈中不可或缺的一个基础组件,接下来我们将看一下,zk应该怎么玩,可以怎么玩


本文作为第一篇,将主要介绍基于zk-client的基本使用姿势,依次来了解下zk的基本概念


I. 准备



1. zk环境安装


用于学习试点目的的体验zk功能,安装比较简单,可以参考博文: 210310-ZooKeeper安装及初体验

wget https://mirrors.bfsu.edu.cn/apache/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
tar -zxvf apache-zookeeper-3.6.2-bin.tar.gz
cd apache-zookeeper-3.6.2-bin
# 前台启动
bin/zkServer.sh start-foreground
复制代码


2. 项目环境


本文演示的是直接使用apache的zookeeper包来操作zk,与是否是SpringBoot环境无关

核心依赖


<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.7.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>
复制代码


版本说明:

  • zk: 3.6.2
  • SpringBoot: 2.2.1.RELEASE


II. ZK使用姿势



1. zk基本知识点


首先介绍下zk的几个主要的知识点,如zk的数据模型,四种常说的节点


1.1 数据模型


zk的数据模型和我们常见的目录树很像,从/开始,每一个层级就是一个节点

每个节点,包含数据 + 子节点


注意:EPHEMERAL节点,不能有子节点(可以理解为这个目录下不能再挂目录)

zk中常说的监听器,就是基于节点的,一般来讲监听节点的创建、删除、数据变更


1.2 节点


  • 持久节点 persistent node
  • 持久顺序节点 persistent sequental
  • 临时节点 ephemeral node
  • 临时顺序节点 ephemeral sequental


注意:


  • 节点类型一经指定,不允许修改
  • 临时节点,当会话结束,会自动删除,且不能有子节点


2. 节点创建


接下来我们看一下zk的使用姿势,首先是创建节点,当然创建前提是得先拿到zkClient

初始化连接


private ZooKeeper zooKeeper;
@PostConstruct
public void initZk() throws IOException {
    // 500s 的会话超时时间
    zooKeeper = new ZooKeeper("127.0.0.1:2181", 500_000, this);
}
复制代码


节点创建方法,下面分别给出两种不同的case


@Service
public class NodeExample implements Watcher {
    /**
     * 创建节点
     *
     * @param path
     */
    private void nodeCreate(String path) {
        // 第三个参数ACL 表示访问控制权限
        // 第四个参数,控制创建的是持久节点,持久顺序节点,还是临时节点;临时顺序节点
        // 返回 the actual path of the created node
        // 单节点存在时,抛异常 KeeperException.NodeExists
        try {
            String node = zooKeeper.create(path + "/yes", "保存的数据".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println("create node: " + node);
        } catch (KeeperException.NodeExistsException e) {
            // 节点存在
            System.out.println("节点已存在: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 带生命周期的节点
        try {
            Stat stat = new Stat();
            // 当这个节点上没有child,且1s内没有变动,则删除节点
            // 实测抛了异常,未知原因
            String node = zooKeeper.create(path + "/ttl", ("now: " + LocalDateTime.now()).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 1000);
            System.out.println("ttl nod:" + node + " | " + stat);
            // 创建已给监听器来验证
            zooKeeper.exists(path + "/ttl", (e) -> {
                System.out.println("ttl 节点变更: " + e);
            });
        } catch (KeeperException.NodeExistsException e) {
            System.out.println("节点已存在: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码


节点创建,核心在于 zooKeeper.create(path + "/yes", "保存的数据".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);


  • 当节点已存在时,再创建会抛异常 KeeperException.NodeExistsException
  • 最后一个参数,来决定我们创建的节点类型
  • todo: 上面实例中在指定ttl时,没有成功,暂未找到原因,待解决


3. 节点存在判断


判断节点是否存在,比较常见了(比如我们在创建之前,可能会先判断一下是否存在)

/**
 * 判断节点是否存在
 */
private void checkPathExist(String path) {
    try {
        // 节点存在,则返回stat对象; 不存在时,返回null
        // watch: true 表示给这个节点添加监听器,当节点出现创建/删除 或者 新增数据时,触发watcher回调
        Stat stat = zooKeeper.exists(path + "/no", false);
        System.out.println("NoStat: " + stat);
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        // 判断节点是否存在,并监听 节点的创建 + 删除 + 数据变更
        // 注意这个事件监听,只会触发一次,即单这个节点数据变更多次,只有第一次能拿到,之后的变动,需要重新再注册监听
        Stat stat = zooKeeper.exists(path + "/yes", this);
        System.out.println("YesStat: " + stat);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
复制代码


注意


核心用法: zooKeeper.exists(path + "/yes", this);

  • 当节点存在时,返回Stat对象,包含一些基本信息;如果不存在,则返回null
  • 第二个参数,传入的是事件回调对象,我们的测试类NodeExmaple 实现了接口 Watcher, 所以直接传的是this
  • 注册事件监听时,需要注意这个回调只会执行一次,即触发之后就没了;后面再次修改、删除、创建节点都不会再被接收到


4. 子节点获取


获取某个节点的所有子节点,这里返回的是当前节点的一级子节点


/**
 * 获取节点的所有子节点, 只能获取一级节点
 *
 * @param path
 */
private void nodeChildren(String path) {
    try {
        // 如果获取成功,会监听 当前节点的删除,子节点的创建和删除,触发回调事件, 这个回调也只会触发一次
        List<String> children = zooKeeper.getChildren(path, this, new Stat());
        System.out.println("path:" + path + " 's children:" + children);
    } catch (KeeperException e) {
        System.out.println(e.getMessage());
    } catch (Exception e) {
        e.printStackTrace();
    }
}
复制代码


5. 数据获取与修改


节点上是可以存储数据的,在创建的时候,可以加上数据;后期可以读取,也可以修改


/**
 * 设置数据,获取数据
 *
 * @param path
 */
public void dataChange(String path) {
    try {
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(path, false, stat);
        System.out.println("path: " + path + " data: " + new String(data) + " : " + stat);
        // 根据版本精确匹配; version = -1 就不需要进行版本匹配了
        Stat newStat = zooKeeper.setData(path, ("new data" + LocalDateTime.now()).getBytes(), stat.getVersion());
        System.out.println("newStat: " + stat.getVersion() + "/" + newStat.getVersion() + " data: " + new String(zooKeeper.getData(path, false, stat)));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
复制代码


在设置数据时,可以指定版本,当version > 0时,表示根据版本精确匹配;如果为-1时,则只要节点路径对上就成


6. 事件监听


监听主要是针对节点而言,前面在判断节点是否存在、修改数据时都可以设置监听器,但是他们是一次性的,如果我们希望长久有效,则可以使用下面的addWatch


public void watchEvent(String path) {
    try {
        // 注意这个节点存在
        // 添加监听, 与 exist判断节点是否存在时添加的监听器 不同的在于,触发之后,依然有效还会被触发, 只有手动调用remove才会取消
        // 感知: 节点创建,删除,数据变更 ; 创建子节点,删除子节点
        // 无法感知: 子节点的子节点创建/删除, 子节点的数据变更
        zooKeeper.addWatch(path + "/yes", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("事件触发 on " + path + " event:" + event);
            }
        }, AddWatchMode.PERSISTENT);
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        // 注意这个节点不存在
        // 添加监听, 与 exist 不同的在于,触发之后,依然有效还会被触发, 只有手动调用remove才会取消
        // 与前面的区别在于,它的子节点的变动也会被监听到
        zooKeeper.addWatch(path + "/no", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("事件触发 on " + path + " event:" + event);
            }
        }, AddWatchMode.PERSISTENT_RECURSIVE);
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 移除所有的监听
    //zooKeeper.removeAllWatches(path, WatcherType.Any, true);
}
复制代码


上面给出了两种case,


  • AddWatchMode.PERSISTENT: 表示只关心当前节点的删除、数据变更,创建,一级子节点的创建、删除;无法感知子节点的子节点创建、删除,无法感知子节点的数据变更
  • AddWatchMode.PERSISTENT_RECURSIVE: 相当于递归监听,改节点及其子节点的所有变更都监听


7. 节点删除


最后再介绍一个基本功能,节点删除,只有子节点都不存在时,才能删除当前节点(和linux的rmdir类似)


/**
 * 删除节点
 */
public void deleteNode(String path) {
    try {
        // 根据版本限定删除, -1 表示不需要管版本,path匹配就可以执行;否则需要版本匹配,不然就会抛异常
        zooKeeper.delete(path, -1);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
复制代码


8. 小结


本文主要介绍的是java侧对zookeeper的基本操作姿势,可以算是zk的入门,了解下节点的增删改,事件监听;


当然一般更加推荐的是使用Curator来操作zk,相比较于apache的jar包,使用姿势更加顺滑,后面也会做对比介绍



相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
3月前
|
XML Java 数据格式
Spring Cloud全解析:注册中心之zookeeper注册中心
使用ZooKeeper作为Spring Cloud的注册中心无需单独部署服务器,直接利用ZooKeeper服务端功能。项目通过`spring-cloud-starter-zookeeper-discovery`依赖实现服务注册与发现。配置文件指定连接地址,如`localhost:2181`。启动应用后,服务自动注册到ZooKeeper的`/services`路径下,形成临时节点,包含服务实例信息。
300 3
|
4月前
|
Java Spring
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
111 3
|
4月前
|
存储 Java Spring
使用Spring Boot和Zookeeper实现服务协调
使用Spring Boot和Zookeeper实现服务协调
|
5月前
|
Java Spring
Spring Boot与Zookeeper的集成应用
Spring Boot与Zookeeper的集成应用
|
5月前
|
Java Maven Spring
Spring Boot中集成ZooKeeper的最佳实践
Spring Boot中集成ZooKeeper的最佳实践
|
6月前
|
Dubbo Java 应用服务中间件
Spring Boot整合Dubbo+Zookeeper实现RPC调用
Spring Boot整合Dubbo+Zookeeper实现RPC调用 技术栈说明 Dubbo:Dubbo作为RPC框架,能在多个服务之间实现远程服务的调用。比如有两个独立的微服务A和B,A服务想要调用B服务时,因为两者不在同个内存空间中,不能直接调用,所以可以通过Dubbo实现这点。 功能和Spring Cloud的Feign相同,两者都是应用于微服务架构的远程调用框架 Zookeeper:作为注册中心去管理Dubbo服务,这点和Eureka、Nacos相同。 概述 通过一个示例说明Dubbo+Zookeeper在Spring Boot中的应用。 现有两个服务provider和con
180 4
|
6月前
|
Java Spring
spring cloud使用zookeeper作为注册中心—consumer
spring cloud使用zookeeper作为注册中心—consumer
51 0
|
前端开发 Java 数据库连接
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
163 2