zookeeper-3 各种使用场景

本文介绍如何使用ZooKeeper作为配置中心,并实现配置文件的动态监听。通过具体代码示例展示ZooKeeper的基本操作,包括节点的创建、读取、更新及删除,并对比原生API与Curator框架的操作差异。

1.zk用作配置文件,并监听配置变化

@Slf4j
public class ConfigCenter {

    private final static  String CONNECT_STR="192.168.25.111:2181";

    private final static Integer  SESSION_TIMEOUT=30*1000;

    private static ZooKeeper zooKeeper=null;

    //因为new ZooKeeper是异步的,底层是2个守护线程,如果没有countDownLatch有可能发生zk还没有
    //连接上,程序就跑到下一步了,也就是相当new zk是一个异步的过程,用countDownLatch可以保证
    //建立连接成功
    private static CountDownLatch countDownLatch=new CountDownLatch(1);

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        zooKeeper=new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                //也就是zk客户端连接后会显示如下信息,这里做一个判断
                //WatchedEvent state:SyncConnected type:None path:null
                    if (event.getType()== Event.EventType.None
                            && event.getState() == Event.KeeperState.SyncConnected){
                        log.info("连接已建立");
                        countDownLatch.countDown();
                    }
            }
        });
        countDownLatch.await();

        MyConfig myConfig = new MyConfig();
        myConfig.setKey("name");
        myConfig.setName("lcc");
        ObjectMapper objectMapper=new ObjectMapper();
        //将默认配置序列化成字节数组
        byte[] bytes = objectMapper.writeValueAsBytes(myConfig);
        //创建一个zkconfig的节点,默认开放权限的持久节点
        String s = zooKeeper.create("/zkconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        //注册一个监听
        Watcher watcher = new Watcher() {
            @SneakyThrows
            @Override
            public void process(WatchedEvent event) {
                //监听zkconfig,当数据发生变化
                if (event.getType()== Event.EventType.NodeDataChanged
                        && event.getPath()!=null && event.getPath().equals("/zkconfig")){
                    log.info(" PATH:{}  发生了数据变化" ,event.getPath());
                    //这里非常关键,因为zk的监听是一次性的,当监听完事件就不在继续监听了,所以这里
                    //需要再次监听
                    byte[] data = zooKeeper.getData("/zkconfig", this, null);
                    //拿到zk配置文件里的数据
                    MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
                    log.info("数据发生变化: {}",newConfig);
                }
            }
        };
        //获取默认的数据
        byte[] data = zooKeeper.getData("/zkconfig", watcher, null);
        MyConfig originalMyConfig = objectMapper.readValue(new String(data), MyConfig.class);
        log.info("原始数据: {}", originalMyConfig);
        TimeUnit.SECONDS.sleep(3600);
    }
}

应用启动,发现客户端已经有了初始化的数据

 现在进行修改:

 idea控制台打印日志:数据发生变化: MyConfig(key=name, name=lcd)

这就很好的实现了配置文件的动态监听

二:原生Api基本操作

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.8</version>
</dependency>
@Slf4j
public class BaseOperations  extends StandaloneBase{

    private String zk_first_node = "/zk-first-node";

    //新建节点,默认权限,临时节点
    @Test
    public void myTestCreate() throws KeeperException, InterruptedException {
        ZooKeeper zooKeeper = getZooKeeper();
        String s = zooKeeper.create(zk_first_node, "first".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        log.info("Create:{}",s);
    }

    @Test
    public void myTestGetData(){
        //只要zk_first_node节点的数据发生变化就会打印日志
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                      if (event.getPath()!=null && event.getPath().equals(zk_first_node)
                              && event.getType()== Event.EventType.NodeDataChanged){
                          log.info(" PATH: {}  发现变化",zk_first_node);
                          try {
                              byte[] data = getZooKeeper().getData(zk_first_node, this, null);
                              log.info(" data: {}",new String(data));
                          } catch (KeeperException e) {
                              e.printStackTrace();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
            }
        };

        try {
            byte[] data = getZooKeeper().getData(zk_first_node, watcher, null);
            log.info(" data: {}",new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //set的时候带上版本号,可以用作乐观锁,和cas,mysql的乐观锁一样
    //也就是set的时候,dataversion会在当前版本自动+1,前提是当前版本=传递进来的版本号,
    //如果高并发的时候传递进来多个相同的版本号,某一时候只能有一个修改成功
    @Test
    public void myTestSetData() throws KeeperException, InterruptedException {
        ZooKeeper zooKeeper = getZooKeeper();
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(zk_first_node, false, stat);
       // int version = stat.getVersion();
        zooKeeper.setData(zk_first_node, "third".getBytes(), 1);
    }

    @Test
    public void myTestDelete() throws KeeperException, InterruptedException {
         // -1 代表匹配所有版本,直接删除
         // 任意大于 -1 的代表可以指定数据版本删除
        getZooKeeper().delete("/config",-1);
    }

    //异步回调,也就是耗时的操作放在异步线程中
    @Test
    public void  myAsyncTest(){
          getZooKeeper().getData("/test", false, (rc, path, ctx, data, stat) -> {
              Thread thread = Thread.currentThread();
              log.info(" Thread Name: {},   rc:{}, path:{}, ctx:{}, data:{}, stat:{}",thread.getName(),rc, path, ctx, data, stat);
        },"test");
        log.info(" over .");
    }
    
}

curator操作zk

// 递归创建子节点,原先老的api创建子节点需要先创建父节点,然后在父节点上创建
//子节点,这里用curator可以递归创建
@Test
public void testCreateWithParent() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    String pathWithParent = "/second-parent/sub";
    //这个操作是同步执行的
    String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
    log.info("curator create node :{}  successfully.", path);
}

 

// protection模式,防止由于异常原因,导致僵尸节点
// 当客户端发送请求给服务器创建节点,服务器接收到节点也创建好了节点,数据落盘到内存中
//但是此时服务器宕机了,客户端没有收到响应,再次发送请求,也就是执行了多次,
//底层是如何保证幂等性的呢?通过在节点上新增一个uuid
@Test
public void testCreate() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    String forPath = curatorFramework
            .create()
            .withProtection()
            .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
                    forPath("/curator-protect-node", "protect-node".getBytes());
    log.info("curator create node :{}  successfully.", forPath);
}

 发现在curator-node前面加上了一个uuid

@Test
public void testGetData() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    byte[] bytes = curatorFramework.getData().forPath("/_c_63335c61-80b5-4bc1-9b10-02b180d125a9-curator-node0000000002");
    log.info("get data from  node :{}  successfully.", new String(bytes));
}

idea控制台结果:

 

//get操作
@Test
public void testGetData() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    byte[] bytes = curatorFramework.getData().forPath("/_c_63335c61-80b5-4bc1-9b10-02b180d125a9-curator-node0000000002");
    log.info("get data from  node :{}  successfully.", new String(bytes));
}

//set操作
@Test
public void testSetData() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    curatorFramework.setData().forPath("/_c_b1ba6d26-d335-476f-85ab-ae65450a3f29-curator-node10000000005", "changed!".getBytes());
    byte[] bytes = curatorFramework.getData().forPath("/_c_b1ba6d26-d335-476f-85ab-ae65450a3f29-curator-node10000000005");
    log.info("get data from  node /curator-node :{}  successfully.", new String(bytes));
}

//删除操作
@Test
public void testDelete() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    String pathWithParent = "/second-parent";
    curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
}

//遍历目录下的所有子节点
//create /discovery
//create /discovery/example
//create -s /discovery/example/x create -s /discovery/example/y create -s /discovery/example/z
@Test
public void testListChildren() throws Exception {
    CuratorFramework curatorFramework = getCuratorFramework();
    String pathWithParent = "/discovery/example";
    List<String> strings = curatorFramework.getChildren().forPath(pathWithParent);
    strings.forEach(System.out::println);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值