ZooKeeper 案例(配置中心/分布式锁/分布式唯一ID)

配置中心案例

场景: 数据库用户信息密码放在一个配置文件中,应该读取配置文件信息,放入缓存。信息改变时需要重新加载,通过ZooKeeper 自动完成缓存同步。

  1. 连接zookeeper服务器
  2. 读取zookeeper中的配置信息,注册watcher监听器,存入本地变量
  3. 当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获变化事件
  4. 重新获取配置信息
public class AutoSyncConfigInfo {

    private String username;
    private ZooKeeper zooKeeper;

    public AutoSyncConfigInfo() throws KeeperException, InterruptedException {
        zooKeeper = ZooKeeperUtils.genZkConn();
        getInfoFromZk();
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    //watcher内部类
    class ConfigWatcher implements Watcher{

        @Override
        public void process(WatchedEvent event) {
            System.out.println("---ConfigWatcher---");
            System.out.println("Path = " + event.getPath());
            System.out.println("EventType = "+event.getType());
            if (event.getType() == Watcher.Event.EventType.None){
                if (event.getState() == Event.KeeperState.SyncConnected){
                    System.out.println("连接成功!");
                    //手动减1
                    count.countDown();
                } else if (event.getState() == Event.KeeperState.Disconnected){
                    System.out.println("连接断开!");
                } else if (event.getState() == Event.KeeperState.Expired){
                    System.out.println("连接超时!自动重新连接。。。");
                    try {
                        zooKeeper = new ZooKeeper("hadoop102",5000,this);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else if (event.getState() == Event.KeeperState.AuthFailed){
                    System.out.println("验证失败!");
                }
            }
            if (event.getType() == Event.EventType.NodeDataChanged){
                //重新从zookeeper获取数据
                getInfoFromZk();
            }
        }
    }

    /**
     * 从zookeeper读取数据
     */
    private void getInfoFromZk(){
        try {
            this.username = new String(zooKeeper.getData("/config/username", new ConfigWatcher(), null));
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void closeZkConn(){
        //zooKeeper.close();
    }

    public static void main(String[] args) throws KeeperException, InterruptedException {
        AutoSyncConfigInfo autoSyncConfigInfo = new AutoSyncConfigInfo();
        for (int i = 0; i < 10; i++) {
            Thread.sleep(3000);
            System.out.println(autoSyncConfigInfo.getUsername());
            System.out.println("-----------------------");
        }
    }
}

分布式唯一ID

在过去的单库单表型系统中,通常第可以使用数据库字段自带的auto_ increment属性来自动为每条记录生成个唯一的ID。但是分库分表后,就无法在依靠数据库的auto_ increment属性来唯一标识一条记录了。此时我们就可以用zookeeper在分布式环境下生成全局唯一ID

  1. 连接zk服务器
  2. 指定路径生成临时有序节点
  3. 取序列号及分布式环境下的唯一ID
public class GlobalUUID {
    private ZooKeeper zooKeeper;
    private String IP = "hadoop102:2181";
    private String path = "/uniqueId";
    private CountDownLatch count = new CountDownLatch(1);

    public GlobalUUID() {
        try {
            zooKeeper = new ZooKeeper(IP,5000,new GlobalIDWatcher());
            //阻塞线程,等待连接成功执行
            count.await();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成分布式唯一ID
     * @return 返回ID
     */
    private String getUUID(){
        String tmpPath = "";
        //创建临时有序节点
        try {
            tmpPath = zooKeeper.create(path,new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
        return tmpPath.substring(path.length());
    }

    public static void main(String[] args) throws InterruptedException {
        GlobalUUID globalUUID = new GlobalUUID();
        //线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,5, 60,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        String lock = new String("");

        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.execute(()->{
                //增加同步
                synchronized (lock) {
                    //System.out.println(Thread.currentThread());
                    System.out.println(globalUUID.getUUID());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        TimeUnit.SECONDS.sleep(50);
        threadPoolExecutor.shutdown();
    }

    //watcher内部类
    class GlobalIDWatcher implements Watcher {

        @Override
        public void process(WatchedEvent event) {
            System.out.println("---GlobalWatcher---");
            System.out.println("Path = " + event.getPath());
            System.out.println("EventType = " + event.getType());
            if (event.getType() == Watcher.Event.EventType.None) {
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    System.out.println("连接成功!");
                    //手动减1
                    count.countDown();
                } else if (event.getState() == Event.KeeperState.Disconnected) {
                    System.out.println("连接断开!");
                } else if (event.getState() == Event.KeeperState.Expired) {
                    System.out.println("连接超时!自动重新连接。。。");
                    try {
                        zooKeeper = new ZooKeeper("hadoop102", 5000, this);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else if (event.getState() == Event.KeeperState.AuthFailed) {
                    System.out.println("验证失败!");
                }
            }
        }
    }
}

结果:

在这里插入图片描述

分布式锁

分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同工具Zookeeper,当然也有着标准的实现方式。下面介绍在zookeeper中如果实现排他锁

设计思路

  1. 每个客户端往/Locks下创建临时有序节点/Locks/Lock_,创建成功后/Locks下面会有每个客户端对应的节点,如/Locks/Lock_000000001
  2. 客户端取得/Locks下子节点,并进行排序,判断排在前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功
  3. 如果自己的锁节点不在第一位,则监听自己前一位的锁节点。例如,自己锁节点Lock_000000002,那么则监听Lock_000000001
  4. 当前一位锁节点(Lock_000000001)对应的客户端执行完成,释放了锁,将会触发监听客户端(Lock_000000002)的逻辑
  5. 监听客户端重新执行第2步逻辑,判断自己是否获得了锁
  6. zookeeper是有工具包的(这里采用手写)

DistributedLock分布式锁对象

package myself.zookeeper.anli;

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

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @program: JavaAPIsLearning
 * @description: 分布式锁
 * @author: CccRJ
 * @create: 2020-09-05 16:39
 **/
public class DistributedLock {
    private String IP = "hadoop102:2181";
    private final String ROOT_LOCK = "/Root_LOCK";
    private final String LOCK_PREFIX = "/LOCK_";
    private final CountDownLatch count = new CountDownLatch(1);
    private final byte[] DATA = new byte[0];

    private ZooKeeper zooKeeper;
    private String path;

    //用来监视上一个节点
    private final Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            //节点被删除
            if (event.getType() == Event.EventType.NodeDeleted) {
                //前一个节点被删除,唤醒
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    };

    public DistributedLock() {
        init();
    }

    /**
     * 初始化连接
     */
    private void init() {
        try {
            zooKeeper = new ZooKeeper(IP, 5000, event -> {
                if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
                    System.out.println("Zookeeper连接成功");
                }
                count.countDown();
            });
            count.await();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建临时有序节点当作锁
     */
    private void createLock() {
        try {
            //创建目录节点
            Stat stat = zooKeeper.exists(ROOT_LOCK, false);
            if (stat == null) {
                //创建根节点
                zooKeeper.create(ROOT_LOCK, DATA, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            //创建临时锁节点
            path = zooKeeper.create(ROOT_LOCK + LOCK_PREFIX, DATA, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println("成功创建节点: " + path);
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }

    }

    /**
     * 初始化链接,创建目录和节点
     */
    public void getLock() {
        init();
        createLock();
        attemptLock();
    }

    /**
     * 获取锁
     */
    public void attemptLock() {
        try {
            //获取所有锁节点
            List<String> children = zooKeeper.getChildren(ROOT_LOCK, false);
            //排序字节点
            Collections.sort(children);
            System.out.println(path + "--当前锁节点:" + children);
            //查看当前节点排名
            int i = children.indexOf(path.substring(ROOT_LOCK.length() + 1));
            if (i == 0) {
                //首位,可以获取
                System.out.println(path.substring(ROOT_LOCK.length() + 1) + "获取锁成功");
                return;
            } else {
                //不是第一位,前面还有锁
                //获取上一个节点路径,进行监视
                String prePath = children.get(i - 1);
                Stat stat = zooKeeper.exists(ROOT_LOCK + "/" + prePath, watcher);
                //获取状态过程中可能前一个节点已经使用完并被删除了,所以需要判空
                if (stat == null) {
                    attemptLock();
                } else {
                    synchronized (watcher) {
                        //等待监视器唤醒
                        watcher.wait();
                    }
                    attemptLock();
                }
            }
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void releaseLock() throws KeeperException, InterruptedException {
        //删除临时有序节点
        zooKeeper.delete(this.path, -1);
        zooKeeper.close();
        System.out.println("锁已经释放:" + this.path);
    }

    public static void main(String[] args) throws InterruptedException {
        DistributedLock distributedLock = new DistributedLock();
        distributedLock.createLock();
        distributedLock.getLock();
    }
}

测试对象

package myself.zookeeper.anli;

import org.apache.zookeeper.KeeperException;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @program: JavaAPIsLearning
 * @description: 测试分布式锁
 * @author: CccRJ
 * @create: 2020-09-05 18:56
 **/
public class TestDistributedLock{
    private void sell(){
        System.out.println("---售票开始---");
        int sleepMillis = 1000;
        try {
            Thread.sleep(sleepMillis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---结束---");
    }

    public void sellTicketWithLock() throws KeeperException, InterruptedException {
        DistributedLock dLock = new DistributedLock();
        dLock.getLock();
        sell();
        dLock.releaseLock();
    }

    public void test1() throws KeeperException, InterruptedException {

        for (int i = 0; i < 10; i++) {
            sellTicketWithLock();
        }
    }

    public void test2(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,2,100,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));
        for (int i = 0; i < 5; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        sellTicketWithLock();
                    } catch (KeeperException | InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    public static void main(String[] args) throws KeeperException, InterruptedException {
        TestDistributedLock test = new TestDistributedLock();
        test.test2();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值