springboot 整合zookeeper框架(curator)

本文介绍了如何在SpringBoot 2.2.7.RELEASE项目中整合Zookeeper 3.5.6和Curator 3.2.0框架。在整合过程中,遇到了版本不兼容的问题,通过降低Curator版本到3.2.0解决了启动报错。此外,还遇到了session超时导致的连接关闭问题,通过调整Zookeeper配置文件中的syncLimit参数为5得以解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、版本 springboot   

2.2.7.RELEASE  jdk 1.8  zookeeper 3.5.6  curator 3.2.0

2、pom文件:

这里只列出zookeeper 和 curator

<dependencies>
        <dependency>
            <groupId>com.zz</groupId>
            <artifactId>zz-log</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!-- zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.5.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- zookeeper 框架  curator-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>3.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>3.2.0</version>
        </dependency>

    </dependencies>

3、工具类:

package com.zz.springbootproject.demo.day3;

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * @description: curator 工具类 练习
 * curator 解决问题:
 * 解决Watch注册一次就会失效的问题
 * 支持直接创建多级结点
 * 提供的 API 更加简单易用
 * 提供更多解决方案并且实现简单,例如:分布式锁
 * 提供常用的ZooKeeper工具类
 * 编程风格更舒服
 * @create: 2020-08-22 14:42
 */
@Slf4j
public class CuratorUtil {
    // 一个zookeeper集群只需要一个 client。劲量保证单例
    private static CuratorFramework client;
    // zk 服务端集群地址
    private String connectString = "192.168.133.129:2181,192.168.133.129:2182,192.168.133.129:2183";

    // session 超时时间
    private int timeOut = 60000;

    // zkclient 重试间隔时间
    private int baseSleepTimeMs = 5000;

    //zkclient 重试次数
    private int retryCount = 5;

    public CuratorUtil() {
        this(null);
    }

    public CuratorUtil(String path) {
        init(path);
    }

    /**
     * @param
     * @Description: 创建客户端
     * retryPolicy,重试连接策略,有四种实现
     * ExponentialBackoffRetry(重试指定的次数, 且每一次重试之间停顿的时间逐渐增加)、
     * RetryNtimes(指定最大重试次数的重试策略)、
     * RetryOneTimes(仅重试一次)、
     * RetryUntilElapsed(一直重试直到达到规定的时间)
     * @Date: 2020-08-22 14:57
     */
    public void init(String path) {
        if (null == client) {
            synchronized (CuratorUtil.this) {
                if (null == client) {
                    client = CuratorFrameworkFactory
                            .builder()
                            .connectString(connectString)
                            .sessionTimeoutMs(timeOut)
                            .retryPolicy(new ExponentialBackoffRetry(baseSleepTimeMs, retryCount))
                            .namespace(path)
                            .build();
                    client.start();
                    log.info("client is created at ================== {}", LocalDateTime.now());
                }
            }
        }
    }

    /**
     * @param
     * @Description: 关闭连接
     * @Date: 2020-08-22 15:15
     */
    public void closeConnection() {
        if (null != client) {
            client.close();
        }
    }

    /**
     * @param path
     * @param value
     * @Description: 创建节点
     * @Date: 2020-08-22 15:15
     */
    public String createNode(String path, String value) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        String node = client
                .create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) // 临时顺序节点
                .forPath(path, value.getBytes());

        log.info("create node : {}", node);
        return node;
    }

    /**
     * @param path
     * @Description: 删除节点信息
     * @Date: 2020-08-22 16:01
     */
    public void deleteNode(String path) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        client
                .delete()
                .deletingChildrenIfNeeded()
                .forPath(path);
        log.info("{} is deleted ", path);
    }


    /**
     * @param path
     * @Description: 获取节点存储的值
     * @Date: 2020-08-22 16:11
     */
    public String getNodeData(String path) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        Stat stat = new Stat();
        byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
        log.info("{} data is : {}", path, new String(bytes));
        log.info("current stat version is {}, createTime is {}", stat.getVersion(), stat.getCtime());
        return new String(bytes);
    }


    /**
     * @Description: 设置节点 数据
     * @param path
     * @param value
     * @Date: 2020-08-22 16:17
     */
    public void setNodeData(String path,String value) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        Stat stat = client.checkExists().forPath(path);
        if(null == stat){
            log.info(String.format("{} Znode is not exists",path));
            throw new RuntimeException(String.format("{} Znode is not exists",path));
        }
        String nodeData = getNodeData(path);
        client.setData().withVersion(stat.getVersion()).forPath(path, value.getBytes());
        log.info("{} Znode data is set. old vaule is {}, new data is {}",path,nodeData,value);
    }


    /**
     * @Description: 创建 给定节点的监听事件  监听一个节点的更新和创建事件(不包括删除)
     * @param path
     * @Date: 2020-08-22 16:47
     */
    public void addWatcherWithNodeCache(String path) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        // dataIsCompressed if true, data in the path is compressed
        NodeCache nodeCache = new NodeCache(client, path,false);
        NodeCacheListener listener = () -> {
            ChildData currentData = nodeCache.getCurrentData();
            log.info("{} Znode data is chagnge,new data is ---  {}", currentData.getPath(), new String(currentData.getData()));
        };
        nodeCache.getListenable().addListener(listener);
        nodeCache.start();
    }


    /**
     * @Description: 监听给定节点下的子节点的创建、删除、更新
     * @param path 给定节点
     * @Date: 2020-08-22 17:14
     */
    public void addWatcherWithChildCache(String path) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        //cacheData if true, node contents are cached in addition to the stat
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client,path,false);
        PathChildrenCacheListener listener = (client, event) -> {
            log.info("event path is --{} ,event type is {}" , event.getData().getPath(), event.getType());
        };
        pathChildrenCache.getListenable().addListener(listener);
        // StartMode : NORMAL  BUILD_INITIAL_CACHE  POST_INITIALIZED_EVENT
        pathChildrenCache.start(PathChildrenCache.StartMode.NORMAL);
    }

    /**
     * @Description: 监听 给定节点的创建、更新(不包括删除) 以及 该节点下的子节点的创建、删除、更新动作。
     * @param path
     * @Date: 2020-08-22 17:35
     */
    public void addWatcherWithTreeCache(String path) throws Exception {
        if (null == client) {
            throw new RuntimeException("there is not connect to zkServer...");
        }
        TreeCache treeCache = new TreeCache(client, path);
        TreeCacheListener listener = (client, event) -> {
            log.info("节点路径 --{} ,节点事件类型: {} , 节点值为: {}" , Objects.nonNull(event.getData()) ? event.getData().getPath() : "无数据", event.getType());
        };
        treeCache.getListenable().addListener(listener);
        treeCache.start();
    }



}

4、遇到的坑

  •   版本问题:zookeeper 和curator版本兼容问题。最开始我curator 版本用的是4.2.0但是启动报错。然后换了3.2.0之后正常启动。
11:04:22.830 [main-EventThread] ERROR org.apache.curator.framework.imps.CuratorFrameworkImpl - Background exception was not retry-able or retry gave up
java.lang.NullPointerException: null
	at org.apache.curator.framework.imps.EnsembleTracker.configToConnectionString(EnsembleTracker.java:185)
	at org.apache.curator.framework.imps.EnsembleTracker.processConfigData(EnsembleTracker.java:206)
	at org.apache.curator.framework.imps.EnsembleTracker.access$300(EnsembleTracker.java:50)
	at org.apache.curator.framework.imps.EnsembleTracker$2.processResult(EnsembleTracker.java:150)
	at org.apache.curator.framework.imps.CuratorFrameworkImpl.sendToBackgroundCallback(CuratorFrameworkImpl.java:883)
	at org.apache.curator.framework.imps.CuratorFrameworkImpl.processBackgroundOperation(CuratorFrameworkImpl.java:653)
	at org.apache.curator.framework.imps.WatcherRemovalFacade.processBackgroundOperation(WatcherRemovalFacade.java:152)
	at org.apache.curator.framework.imps.GetConfigBuilderImpl$2.processResult(GetConfigBuilderImpl.java:222)
	at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:598)
	at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:510)
  • session超时,连接关闭报错。解决办法:修改配置文件:syncLimit=5
Unable to reconnect to ZooKeeper service, session 0x2000000848e0006 has expired
INFO org.apache.zookeeper.ClientCnxn - EventThread shut down for session: 0x2000000848e0006

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值