Zookeeper(5)Zookeeper的使用案例

212 阅读2分钟

1.使用Zookeeper作为注册中心

Zookeeper可以用作配置中心,当我们的配置发生变化时,可以通知客户端,更新配置信息

Zookeeper配置中心的原理:

( 1)连接zookeeper服务器

(2)读取zookeeper中的配置信息,注册watcher监听器,存入本地变量

(3)当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获数据变化事件

(4)重新获取配置信息

public class c12作为配置中心  implements Watcher {

    String ip = "mymv:2181";
    CountDownLatch countDownLatch = new CountDownLatch(1);
    static ZooKeeper zooKeeper;

    //本地化配置信息
    private String url;
    private String username;
    private String password;

    @Override
    public void process(WatchedEvent event) {
        try {
            // 捕获事件状态
            if (event.getType() == Event.EventType.None) {
                if (event.getState() == Event.KeeperState.SyncConnected)
                {
                    System.out.println("连接成功");
                    countDownLatch.countDown();
                } else if (event.getState() == Event.KeeperState.Disconnected) {
                    System.out.println("连接断开!");
                } else if (event.getState() == Event.KeeperState.Expired)
                {
                    System.out.println("连接超时!");
                    // 超时后服务器端已经将连接释放,需要重新连接服务器端
                    zooKeeper = new ZooKeeper("192.168.60.130:2181", 6000, new c12作为配置中心());
                } else if (event.getState() ==
                        Event.KeeperState.AuthFailed) {
                    System.out.println("验证失败!");
                }
            // 当配置信息发生变化时
            } else if (event.getType() == Event.EventType.NodeDataChanged) {
                initValue();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // 构造方法
    public c12作为配置中心() {
        initValue();
    }
    // 连接zookeeper服务器,读取配置信息
    public void initValue() {
        try {
            // 创建连接对象
            zooKeeper = new ZooKeeper(ip, 5000, this);
            // 阻塞线程,等待连接的创建成功
            countDownLatch.await();
            // 读取配置信息
            this.url = new String(zooKeeper.getData("/config/url", true, null));
            this.username = new String(zooKeeper.getData("/config/username", true, null));
            this.password = new String(zooKeeper.getData("/config/password", true, null));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        c12作为配置中心 configCenter = new c12作为配置中心();
        try{
            for (int i = 1; i <= 20; i++) {
                Thread.sleep(5000);
                System.out.println("url:" + configCenter.getUrl());
                System.out.println("username:" + configCenter.getUsername());
                System.out.println("password:" + configCenter.getPassword());
                System.out.println("########################################");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public String getUrl() {
        return url;
    }
    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
}

2.生成唯一的分布式ID

在对数据库进行分表操作时候,需要保证我们的数据在不同的表中拥有唯一的id,通过Zookeeper的序列化临时节点,可以完成全局唯一id的生成

原理: (1)连接zookeeper服务器 (2)指定路径生成临时有序节点 (3)取序列号及为分布式环境下的唯一ID

public class c13生成唯一id implements Watcher {

    String ip = "mymv:2181";
    CountDownLatch countDownLatch =new CountDownLatch(1);
    ZooKeeper zooKeeper;

    @Override
    public void process(WatchedEvent event) {
        try {
            // 捕获事件状态
            if (event.getType() == Event.EventType.None) {
                if (event.getState() == Event.KeeperState.SyncConnected)
                {
                    System.out.println("连接成功");
                    countDownLatch.countDown();
                } else if (event.getState() == Event.KeeperState.Disconnected) {
                    System.out.println("连接断开!");
                } else if (event.getState() == Event.KeeperState.Expired)
                {
                    System.out.println("连接超时!");
                    // 超时后服务器端已经将连接释放,需要重新连接服务器端
                    zooKeeper = new ZooKeeper(ip, 6000, new c12作为配置中心());
                } else if (event.getState() ==
                        Event.KeeperState.AuthFailed) {
                    System.out.println("验证失败!");
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public c13生成唯一id() {
        try {
            //打开连接
            zooKeeper = new ZooKeeper(ip, 5000, this);
            // 阻塞线程,等待连接的创建成功
            countDownLatch.await();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // 生成id的方法
    public String getUniqueId() {
        String path = "";
        try {
            //创建临时有序节点
            path = zooKeeper.create("/uniuqeId", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return path.substring(9);
    }

    public static void main(String[] args) {
        c13生成唯一id unique = new c13生成唯一id();
        for (int i = 0; i < 10; i++) {
            System.out.println(unique.getUniqueId());
        }
    }
}

3.分布式锁

通过zookeeper的有序节点,可以实现分布式锁

实现排它锁原理:

(1)每个客户端往/Locks下创建临时有序节点/Locks/Lock000000001

(2)客户端取得/Locks下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功

(3)如果自己的锁节点不在第一位,则监听自己前一位的锁节点。

(4)当前一位锁节点的状态发生变化时,会通知监听客户端重新执行第2步逻辑,判断自己是否获得了锁

实现共享锁原理:

(1)每个客户端往/Locks下创建临时有序节点/Locks/Lock000000001,并指明节点的内容为R,或者W

(2)客户端取得/Locks下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功

(3)如果自己的锁节点不在第一位,则查看比自己小的节点是否都为R,如果为R,则可以获取锁,否者则监听自己前一位的锁节点。

(4)当前一位锁节点的状态发生变化时,会通知监听客户端重新执行第2步逻辑,判断自己是否获得了锁

public class c14分布式锁 implements Watcher {

    String ip = "mymv:2181";
    CountDownLatch countDownLatch =new CountDownLatch(1);
    ZooKeeper zooKeeper;


    private static final String LOCK_ROOT_PATH = "/Locks";
    private static final String LOCK_NODE_NAME = "Lock_";
    private String lockPath;

    @Override
    public void process(WatchedEvent event) {
        try {
            // 捕获事件状态
            if (event.getType() == Watcher.Event.EventType.None) {
                if (event.getState() == Watcher.Event.KeeperState.SyncConnected)
                {
                    System.out.println("连接成功");
                    countDownLatch.countDown();
                } else if (event.getState() == Watcher.Event.KeeperState.Disconnected) {
                    System.out.println("连接断开!");
                } else if (event.getState() == Watcher.Event.KeeperState.Expired)
                {
                    System.out.println("连接超时!");
                    // 超时后服务器端已经将连接释放,需要重新连接服务器端
                    zooKeeper = new ZooKeeper(ip, 6000, new c12作为配置中心());
                } else if (event.getState() ==
                        Watcher.Event.KeeperState.AuthFailed) {
                    System.out.println("验证失败!");
                }
            }else if (event.getType() == Event.EventType.NodeDeleted) {

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public c14分布式锁() {
        try {
            //打开连接
            zooKeeper = new ZooKeeper(ip, 500000, this);
            // 阻塞线程,等待连接的创建成功
            countDownLatch.await();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void acquireLock() throws Exception {
        //创建锁节点
        createLock();
        //尝试获取锁
        attemptLock();
    }

    //创建锁节点
    private void createLock() throws Exception {
        //判断Locks是否存在,不存在创建
        Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, false);
        if (stat == null) {
            zooKeeper.create(LOCK_ROOT_PATH, new byte[0],
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        // 创建临时有序节点
        lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME,
                new byte[0],
                ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("节点创建成功:" + lockPath);
    }

    //尝试获取锁
    private void attemptLock() throws Exception {
        // 获取Locks节点下的所有子节点
        List<String> list = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
        // 对子节点进行排序
        Collections.sort(list);
        // /Locks/Lock_000000001
        int index = list.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
        if (index == 0) {
            System.out.println("获取锁成功!");
            return;
        } else {
            // 上一个节点的路径
            String path = list.get(index - 1);
            Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + path, this);
            if (stat == null) {
                attemptLock();
            } else {
                synchronized (this) {
                    this.wait();
                }
                attemptLock();
            }
        }
    }

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

    public static void main(String[] args) {
        try {
            c14分布式锁 myLock = new c14分布式锁();
            myLock.acquireLock();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}