Zookeeper入门介绍

Zookeeper是什么

        官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

        简单来说,zookeeper=文件系统+监听通知机制。

1.文件系统

        Zookeeper维护一个类似文件系统的数据结构:

         每个子目录项如 NameService 都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。

 有四种类型的znode:

  • PERSISTENT-持久化目录节点:客户端与zookeeper断开连接后,该节点依旧存在。

  • PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点:客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号。

  • EPHEMERAL-临时目录节点:客户端与zookeeper断开连接后,该节点被删除。

  • EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点:客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号。

2.监听通知机制

        客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

Zookeeper能做什么

        zookeeper功能非常强大,可以实现诸如分布式应用配置管理、统一命名服务、状态同步服务、集群管理等功能,我们这里拿比较简单的分布式应用配置管理为例来说明。

        假设我们的程序是分布式部署在多台机器上,如果我们要改变程序的配置文件,需要逐台机器去修改,非常麻烦,现在把这些配置全部放到zookeeper上去,保存在 zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 zookeeper 的通知,然后从 zookeeper 获取新的配置信息应用到系统中。    

 Zookeeper java实现

         pom依赖:

<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.5.7</version>
</dependency>

        注意POM中zookeeper的版本应该与zookeeper服务的版本一致。

        java现实读取某个文件数据:

package com.redistext.zookeeper;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.stereotype.Component;

import java.util.concurrent.CountDownLatch;

/**
 * @author: maorui
 * @description: TODO
 * @date: 2021/10/19 19:58
 * @description: V1.0
 */
public class ZkClient implements Watcher{

    public static ZooKeeper zooKeeperClient = null;
    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
    private static Stat stat = new Stat();

    
    public static void main(String[] args) throws Exception{
        //连接zookeeper并且注册一个默认的监听器
        zooKeeperClient = new ZooKeeper("10.192.140.1:2181", 5000, new ZkClient());
        //等待zk连接成功的通知
        connectedSemaphore.await();
        //获取path目录节点的配置数据,并注册默认的监听器
        System.out.println(new String(zooKeeperClient.getData("/maorui", true, stat)));
        Thread.sleep(Integer.MAX_VALUE);
    }


    @Override
    public void process(WatchedEvent watchedEvent) {
        if (Event.KeeperState.SyncConnected == watchedEvent.getState()){//zk连接成功通知事件
            if (Event.EventType.None == watchedEvent.getType() && null == watchedEvent.getPath()){
                connectedSemaphore.countDown();
                System.out.println("注册zk客户端成功");
            }else if (watchedEvent.getType() == Event.EventType.NodeDataChanged){//zk目录节点数据变化通知事件
                try {
                    System.out.println("配置已修改,新值为:" + new String(zooKeeperClient.getData(watchedEvent.getPath(), true, new Stat())));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

        程序启动,读取到了zookeeper的/maorui目录节点下的数据'maorui'。

         打开一个客户端,修改/maorui下面的数据。

         修改完成后,代码中接收到了数据的变化

        zookeeper实现简单的分布式锁:

        分布式锁实现类:

package com.redistext.controller;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author: maorui
 * @description: TODO
 * @date: 2021/10/21 11:14
 * @description: V1.0
 */
public class DistributedLock {

    private final ZooKeeper zk;
    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
    private static CountDownLatch waitSemaphore = new CountDownLatch(1);

    private String waitPath;

    private String currentMode;

    public DistributedLock() throws Exception{

        //获取连接
        zk = new ZooKeeper("10.192.140.1:2181", 2000, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                //connectedSemaphore 如果连接上zk 可以释放掉
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
                    connectedSemaphore.countDown();
                }

                //waitSemaphore 需要释放
                if (watchedEvent.getType() == Event.EventType.NodeDeleted  && watchedEvent.getPath().equals(waitPath)){
                    waitSemaphore.countDown();
                }

            }
        });

        //等待zk连接正常后,忘下走程序
        connectedSemaphore.await();

        //判断根节点/locks是否存在
        Stat exists = zk.exists("/locks", false);
        if (exists == null){
            //创建跟节点
            zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    //对zk加锁
    public void zkLock() throws Exception{
        //创建对应的临时带序号节点
        currentMode = zk.create("/locks/seq-001", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        //判断创建的节点是否是最小的序号节点,如果是获取到锁;如果不是,监听它序号的前一个节点
        List<String> children = zk.getChildren("/locks", false);

        //如果children只有一个值,那就直接获取锁;如果有多个节点,需要判断谁最小
        if (children.size() == 1){
            return;
        }else {
            //排序
            Collections.sort(children);

            //获取节点名称
            String thisNode = currentMode.substring("/locks/".length());
            //通过seq-001获取该节点在children中的位置
            int index = children.indexOf(thisNode);
            if (index == -1){
                System.out.println("数据异常");
            }else if (index == 0){
                //只有一个值,可以获取锁了
                return;
            }else {
                //需要监听它前一个节点变换
                waitPath = "/locks/" + children.get(index -1);
                zk.getData(waitPath, true, null);
                //等待监听
                waitSemaphore.await();
                return;
            }

        }


    }

    public void unZkLock() throws Exception{

        //删除节点
        zk.delete(currentMode, -1);

    }


}

        分布式锁测试类:

package com.redistext.controller;

/**
 * @author: maorui
 * @description: TODO
 * @date: 2021/10/21 14:35
 * @description: V1.0
 */
public class DistributedLockTest {

    public static void main(String[] args) throws Exception{

        DistributedLock lock1 = new DistributedLock();

        DistributedLock lock2 = new DistributedLock();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock1.zkLock();
                    System.out.println("线程1启动,获取到锁");
                    Thread.sleep(5000);
                    lock1.unZkLock();
                    System.out.println("线程1释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.zkLock();
                    System.out.println("线程2启动,获取到锁");
                    Thread.sleep(5000);
                    lock2.unZkLock();
                    System.out.println("线程2释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

        测试结果:

 

参考文档:

        Zookeeper入门看这篇就够了_技术分享-CSDN博客_zookeeper

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值