81-java-zookeeper(1) -zookeeper单机/原生api/应用场景

本文的项目:zookeeper-demo1

一.简介

1.功能及用途
  • 1.维护配置信息
  • 2.分布式锁
  • 3.集群管理
  • 4.生成分布式唯一id
2.设计目标及优点

优点

  • 1.高性能
    内存,存储数据
  • 2.高可用
    集群部署
  • 3.严格顺序访问
    生成的全局唯一的id,分布式锁

对比redis

rediszookeeper
存储类型内存内存
数据类型k-v树形结构
性能
高可用
目标数据缓存分布式辅助功能
应用场景缓存数据库分布式锁/配置中心

设计目标
由于zookeeper设计时的目标就是作为分布式辅助功能,所以zookeeper采用的是树形结构,而不是速度更快的k-v结构,这就注定了zookeeper的引用场景不是作为缓存,虽然zookeeper也可以作为缓存.

二.数据模型

1.数据模型结构
  • redis是使用k-v结构
  • zookeeper是使用树状结构,类似于linux的文件结构 节点的表示
    /xxx/yyy
2.节点的属性
	1.cZxid : 数据节点创建时的事务ID
	2.从Time : 节点创建的时间
	3.mZxid : 节点最后一次更新是的事务ID
	4.mTime : 节点最后一次更新的时间
	5.pZxid : 节点最后一次呗修改时的事务ID
	6.cversion : 子节点的更改次数
	7.dataVersion :节点数据更改的次数
	8.aclVersion : 节点ACL的更改次数
	9.ephemeralOwer : 表示是临时节点还是永久节点;临时节点为会话的SessionID,持久化节点为0;
	10.dataLength :数据长度
	11.numChildren :子节点个数

三.单机安装

1.在linux上使用root用户创建zookeeper用户,密码可设置为: zookeeper

  • useradd zookeeper
    #添加用户
  • passwd zookeeper
    #添加密码

2.安装JDK,因为zookeeper是java开发的,所以zookeeper依赖于JDk

  • 上传JDK
  • 解压JDK : tar -xvf jdk-8u51-linux-x64.tar.gz

3.配置java环境变量
配置环境变量: vim /etc/profile
在这里插入图片描述

4.检验jdk安装是否成功
检查java环境变量是否配置成功: java -version
在这里插入图片描述
5.上传解压zookeeper

  • 上传zookeeper文件
  • 解压zookeeper: tar -xvf zookeeper-3.4.8.tar.gz

6.为zookeeper准备配置文件

  • 在根目录下,创建一个data和log目录 : mkdir data log
    在这里插入图片描述
  • 进入当前zookeeper根目录的 conf目录
  • 复制配置文件: cp zoo_sample.cfg zoo.cfg
  • 编辑配置文件: vim zoo.cfg
    1.端口号
    2.修改data目录 ,指定data目录为我们刚刚创建的data目录
    3.设置log目录,指定log目录为刚刚我们创建的log目录
    在这里插入图片描述
  • 启动zookeeper
    1.进入zookeeper根目录下的 bin目录
    2.启动服务器: sh zkServer.sh start [zoo1.cfg] #可以指定配置文件,不指定默认是在conf目录下的zoo.cfg配置文件

四.zookeeper常用命令(都是在bin目录下)

1.服务器相关命令

(1) 启动服务器:

  • sh zkServer.sh start [zoo.cfg]
    #可以指定配置文件,不指定默认是在conf目录下的zoo.cfg配置文件

(2) 关闭服务器:

  • sh zkServer stop [zoo.cfg]
    #可以指定配置文件,不指定默认是在conf目录下的zoo.cfg配置文件

(3)查看当前服务器状态:

  • sh zkServer status [zoo.cfg]

    #可以指定配置文件,不指定默认是在conf目录下的zoo.cfg配置文件

(4)登陆服务器

  • sh zkCli.sh
    #默认登陆的是本地的2181端口的服务器

  • sh zkCli.sh -server 127.0.0.1:2182
    #登陆到其他服务器

2.数据操作(先登陆到服务上)
(1)新增节点

create [-s] [-e] path data
#其中-s为有序节点,-e为临时节点,默认为持久化节点

  • 持久化节点
    1.创建: create /a “a”
    2.取值: get /a

  • 有序节点
    1.创建: create -s /b “b”
    2.取值: get /a0000000003
    #/a0000000003 这里的是代指我们创建时,服务器分配给我的节点号
    常用来作为分布式的全局id

  • 临时节点(会话过期之后被删除)
    1.创建: create -e /c cc
    2.取值: get /c

  • 临时有序节点
    1.创建: create -s -e /aa “aa”
    2.取值: get /aa00000001
    # /aa00000001这个是服务器给我们分配的节点号;常用来作为分布式事务锁,会话结束就消失,保证不会死锁,有序保证分布式有序行

(2)修改节点:
  • set path data [数据版本号]
    #修改节点
(3)删除节点
  • delete path [数据版本号]
    #删除节点(该节点没有子节点) :

  • rmr path
    #删除该节点及其子节点

(4)查看节点和状态
  • get path
    #返回属性和数据

  • stat path
    #只返回该节点的属性,不包含数据

(4)查看节点列表
  • ls path
    #查看节点的所有子节点列表
(5)监听器(一次监听器,只能监听一次变化,用了就失效了)
  • get path watch
    #监听该节点的数据变化

  • ls/ls2 path watch
    #监听子节点发生变化
    作用:监听配置的变化,如果配置发生变化,则我们需要重新获取配置

(6)权限控制(针对节点操作的权限)

1)授权的三个主要特性

  • 授权模式 : 授权的策略
  • 授权对象
  • 权限

2)授权模式

  • world
    #默认的,任何人都可操作

  • ip
    #指定ip才能操作

  • auth
    #使用已添加认证的用户

  • digest
    #用户+密码的方式

3)权限

  • create c
    #可以在该节点下创建子节点
  • delete d
    #可以删除子节点(仅仅是子节点,不能删除当前节点)
  • read r
    #可以读取该节点及其子节点
  • write w
    #可以设置该节点数据
  • admin a
    #可以设置节点访问控制的权限

4)权限相关语法

[1] 授权语法
setAcl path acl
#acl表示的授权模式+授权对象+具体的权限

  • world授权: setAcl path world:anyone:drwa
    #设置该节点的权限为任何人可以drwa(删除/读/写/修改权限)的操作.

  • ip授权:
    setAcl path ip:192.168.10.100:cdrwa[,ip:192.168.10.101:cdrwa]
    #授予这个IP地址cdrwa权限.[]方括号内表示添加多个ip授权,则用逗号分开

  • author授权

    • 登陆授权用户: addauth digest 用户名:密码
      #不登录则无法获取该节点数据

    • 授权: setAcl path auth:用户名:密码:wa
      #针对用户wa权限,要获取该节点数据之前,则必须登陆

  • digest授权

    1.先获取密码的密文
    2.授权: setAcl path digest:用户名:密码:wa #针对用户wa权限,密码必须使用密文

  • 同时使用多种授权模式
    setAcl path world:anyone:drwa,ip:192.168.10.100:cdrwa,auth:用户名:密码:wa
    #多种授权模式.其实就是中间用逗号分隔开

  • 超级管理员
    1.生成超级管理员的密码密文
    2.在脚本文件 zhServer.sh 中增加我们的超级管理员

[2]读取acl权限:
getAcl path

[3] 添加授权用户:
addauth digest 用户名:密码

五.zookeeper原生api

1.下载linux服务器上的zookeeper根目录下的zookeeper-3.4.14.jar和lib目录下的所有jar包

sz 文件名

2.将这些jar包导入我们的项目中
3.使用api
  • 1)zookeeper连接对象的创建
 try {
            //创建计数器对象
            CountDownLatch countDownLatch = new CountDownLatch(1);
            /**
             * 参数一:IP地址及端口
             * 参数二:超时时间(毫秒)
             * 参数三:监视器
             */
            //连接单个zookeeper
            //ZooKeeper zooKeeper = new ZooKeeper("118.190.58.27:2181", 5000, new Watcher() {
            //连接集群,只需要在ip中用逗号隔开即可
            ZooKeeper zooKeeper = new ZooKeeper("118.190.0.27:2181,118.190.0.27:2182,118.190.0.27:2183", 5000, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if(event.getState() == Event.KeeperState.SyncConnected){
                        System.out.println("连接创建成功!!");
                        //执行计数减1的,因为原来就是1,减去1就等于0了,所以countDownLatch.await()阻塞的代码就不阻塞了
                        countDownLatch.countDown();
                    }
                }
            });
            //主线程阻塞等待连接对象的创建,当计数变为0时,则继续往下执行
            countDownLatch.await();

            //会话编号
            System.out.println("当前会话id:" + zooKeeper.getSessionId());
            zooKeeper.close();
        }catch (Exception e){
            e.printStackTrace();
        }
  • 2)创建节点对象

     	zooKeeper.create(...)
    
public class ZKcreate {
    private ZooKeeper zooKeeper;
    private String ip = "118.190.0.27:2181";

    /**
     * 构建zookeeper对象
     */
    @Before
    public void before(){
        try {
            //创建计数器对象
            CountDownLatch countDownLatch = new CountDownLatch(1);
            /**
             * 参数一:IP地址及端口
             * 参数二:超时时间(毫秒)
             * 参数三:监视器
             */
            zooKeeper = new ZooKeeper(ip, 5000, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if(event.getState() == Event.KeeperState.SyncConnected){
                        System.out.println("连接创建成功!!");
                        countDownLatch.countDown();
                    }
                }
            });
            //主线程阻塞等待连接对象的创建
            countDownLatch.await();

            //会话编号
            System.out.println("当前会话id:" + zooKeeper.getSessionId());

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

    /**
     * 关闭zookeeper连接
     */
    @After
    public void after(){
        try {
            zooKeeper.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 同步创建zookeeper节点
     * 设置world:anyone权限
     */
    @Test
    public void create1() throws Exception {
        /**
         * 参数一: 节点(该节点的上一个节点必须存在)
         * 参数二: 节点数据
         * 参数三: 权限列表 'world,'anyone
         * 参数四: 创建持久化节点
         */
        String s = zooKeeper.create("/create/node1", "node1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("创建成功!!!");
    }

    /**
     * 同步创建zookeeper节点
     * 设置create和write权限
     */
    @Test
    public void create2() throws Exception{
        //授权列表
        ArrayList<ACL> list = new ArrayList<>();
        //授权模式和授权对象
        Id id = new Id("world", "anyone");
        list.add(new ACL(ZooDefs.Perms.CREATE,id));
        list.add(new ACL(ZooDefs.Perms.WRITE,id));
        String s = zooKeeper.create("/create/node2", "node2".getBytes(), list, CreateMode.PERSISTENT);
        System.out.println("创建成功!!!");

    }

    /**
     * 同步创建zookeeper节点
     * 根据ip设置权限
     */
    @Test
    public void create3() throws Exception{
        //授权列表
        ArrayList<ACL> list = new ArrayList<>();
        //授权模式和授权对象
        Id id = new Id("ip", "192.168.0.101");
        list.add(new ACL(ZooDefs.Perms.ALL,id));
        String s = zooKeeper.create("/create/node3", "node3".getBytes(), list, CreateMode.PERSISTENT);
        System.out.println("创建成功!!!");

    }

    /**
     * 同步创建zookeeper节点
     * 根据auth设置权限
     */
    @Test
    public void create4() throws Exception{
        zooKeeper.addAuthInfo("digest","gl:123456".getBytes());
        String s = zooKeeper.create("/create/node4", "node4".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
        System.out.println("创建成功!!!");

    }

}

  • 3)修改节点对象

     	zooKeeper.setData(...)
    
  /**
     * 同步修改节点
     */
    @Test
    public void set1() throws Exception {
        /**
         * 参数一: 节点path
         * 参数二: 节点数据
         * 参数三: 版本  版本不一致,则修改不了,-1表示当前最新的版本,不指定版本号
         */
        Stat stat = zooKeeper.setData("/set/node1", "111".getBytes(), -1);
        System.out.println(stat);
    }


    /**
     * 异步修改节点
     */
    @Test
    public void set2() throws Exception {
        /**
         * 参数一: 节点path
         * 参数二: 节点数据
         * 参数三: 版本  版本不一致,则修改不了,-1表示当前最新的版本,不指定版本号
         */
        zooKeeper.setData("/set/node1", "222".getBytes(), -1, new AsyncCallback.StatCallback() {
            /**
             *
             * @param rc 是否成功  0代表修改成功
             * @param path 节点路劲
             * @param ctx 上下文参数对象
             * @param stat 属性表示对象
             */
            @Override
            public void processResult(int rc, String path, Object ctx, Stat stat) {
                System.out.println("rc:" + rc);
                System.out.println("path:" + path);
                System.out.println("ctx:" + ctx);
                System.out.println("stat:" + stat);
            }
        },"i am context");
        Thread.sleep(1000);
        System.out.println("结束");
    }
  • 4)删除节点对象

     	zooKeeper.delete(...)
    
 /**
     * 同步删除节点
     */
    @Test
    public void delete() throws KeeperException, InterruptedException {
        zooKeeper.delete("/delete/node1",-1);
        System.out.println("删除成功");
    }

    /**
     * 异步删除节点
     */
    @Test
    public void delete2() throws KeeperException, InterruptedException {
        zooKeeper.delete("/delete/node2", -1, new AsyncCallback.VoidCallback() {
            /**
             *
             * @param rc 删除是否成功  0代表删除成功过
             * @param path 删除成功
             * @param ctx 上下文对象
             */
            @Override
            public void processResult(int rc, String path, Object ctx) {
                System.out.println("rc:" + rc);
                System.out.println("path:" + path);
                System.out.println("ctx:" + ctx);
            }
        }, "i am context");

        Thread.sleep(1000);
        System.out.println("删除成功");
    }

  • 5)获取节点对象

     	zooKeeper.getData(...)
    
 /**
     * 同步获取节点数据
     */
    @Test
    public void get1() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData("/get/node1", false, stat);
        System.out.println(new String(data));
        System.out.println("version:" + stat.getVersion());
    }


    /**
     * 异步获取节点数据
     */
    @Test
    public void get2() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        zooKeeper.getData("/get/node1", false, new AsyncCallback.DataCallback() {

            /**
             * @param rc 0代表成功
             * @param path 节点路劲
             * @param ctx 上下文对象
             * @param data 数据
             * @param stat 属性对象
             */
            @Override
            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
                System.out.println("rc:" + rc);
                System.out.println("path:" + path);
                System.out.println("ctx:" + ctx);
                System.out.println("data:" + new String(data));
                System.out.println("stat:" + stat);
            }
        },"i am context");
        Thread.sleep(1000);
        System.out.println("version:" + stat.getVersion());
    }
  • 6)获取子节点对象

     	zooKeeper.getChildren(...)
    
 /**
     * 同步获取子节点数据
     */
    @Test
    public void get1() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        List<String> data = zooKeeper.getChildren("/get", false, stat);
        for (String datum : data) {
            System.out.println(datum);
        }
        System.out.println("version:" + stat);
    }


    /**
     * 异步获取子节点数据
     */
    @Test
    public void get2() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        zooKeeper.getChildren("/get", false, new AsyncCallback.ChildrenCallback() {
            /**
             * @param rc 0代表成功
             * @param path 节点路劲
             * @param ctx 上下文对象
             * @param children 子节点数据
             */
            @Override
            public void processResult(int rc, String path, Object ctx, List<String> children) {
                System.out.println("rc:" + rc);
                System.out.println("path:" + path);
                System.out.println("ctx:" + ctx);
                for (String child : children) {
                    System.out.println(child);
                }
            }
        }, "i am context");
        Thread.sleep(1000);
        System.out.println("version:" + stat.getVersion());
    }
  • 7)判断子节点对象是否存在

     	 zooKeeper.exists(...)
    

    /**
     * 同步判断该节点是否存在
     * stat: null表示不存在,否则是存在
     */
    @Test
    public void exist1() throws KeeperException, InterruptedException {

        Stat stat = zooKeeper.exists("/get1", false);
        System.out.println(stat);
    }


    /**
     * 异步判断该节点是否存在
     * stat: null表示不存在,否则是存在
     */
    @Test
    public void exist2() throws KeeperException, InterruptedException {

        zooKeeper.exists("/get", false, new AsyncCallback.StatCallback() {
            /**
             * @param rc 0代表成功
             * @param path 节点路劲
             * @param ctx 上下文对象
             * @param stat 节点状态对象
             */
            @Override
            public void processResult(int rc, String path, Object ctx, Stat stat) {
                System.out.println("rc:" + rc);
                System.out.println("path:" + path);
                System.out.println("ctx:" + ctx);
                System.out.println("stat:" + stat);
            }
        },"i am context");

        Thread.sleep(1000);
    }

五.zookeeper的事件监听

1.概念
	监听某个节点的变化
2.架构
3.特性
  • 1.一次性
    #watcher是一次性的,一旦被触发就会被移除,再次使用需要重新注册

  • 2.客户端顺序回调
    #watcher回调顺序是串行化执行的,只有回调后客户端才能看到最新的数据状态,一个watcher回调逻辑不应该太多.

  • 3.轻量级
    #watchEvent是最小的通信单元,结构上只包含通知状态,时间类型和节点路劲,并不会告诉数据节点变化前后的具体内容

  • 4.时效性
    #watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍然可接受到通知

4.监听节点事件的类型
	1.None   
	2.NodeCreated     #监听子节点被创建
	3.Nodedelete    #当前节点被删除
	4.NodeDataChanged    #监听节点数据变化
	5.NodeChildrenChanged     #监听子节列表发生变化
5.watcher的通知类型
	1.SyncConnected   客户端与服务器建立连接时
	2.DisConnected    #客户端与服务器断开连接时
	3.Expired    #会话session失效时
	4.AuthFailed    #身份认证失败时
6.javaAPI注册watcher
	exist 方法注册watcher
	getData方法注册watcher
	getChildren 方法注册watcher

exists方法注册watcher

public class ZKWathcerExists {
    private ZooKeeper zooKeeper = null;
    private String ip = "118.190.00.27:2181";

    @Before
    public void before() throws Exception{
        CountDownLatch countDownLatch = new CountDownLatch(1);
        //连接zookeeper
        zooKeeper = new ZooKeeper(ip, 6000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("连接对象的额参数");
                if(event.getState() == Event.KeeperState.SyncConnected){
                    countDownLatch.countDown();
                }
                System.out.println("path:" + event.getPath());
                System.out.println("eventType:" + event.getType());
            }
        });
        countDownLatch.await();
    }

    @After
    public void after() throws InterruptedException {
        System.out.println("结束");
        zooKeeper.close();
    }

    /**
     * 监听 /watcher节点.当节点发生变化的时候,返回变化类型
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void watcher1() throws KeeperException, InterruptedException {
        //使用连接时的watcher监听
        Stat exists = zooKeeper.exists("/watcher", true);
        Thread.sleep(5000);
    }

    /**
     * 监听 /watcher节点.当节点发生变化的时候,返回变化类型
     * 值监听一次
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void watcher2() throws KeeperException, InterruptedException {
        //使用自定义的watcher监听
        Stat exists = zooKeeper.exists("/watcher", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("path:" + event.getPath());
                System.out.println("type:" + event.getType());
            }
        });
        Thread.sleep(10000);
    }

    /**
     * 监听 /watcher节点.当节点发生变化的时候,返回变化类型
     * 对watcher整个生命周期都监听,监听多个事件
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void watcher3() throws KeeperException, InterruptedException {
        //使用自定义的watcher监听
        Stat exists = zooKeeper.exists("/watcher", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println("path:" + event.getPath());
                System.out.println("type:" + event.getType());
                try {
                    zooKeeper.exists("/watcher",this);
                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        });
        Thread.sleep(30000);
    }

    /**
     * 监听 /watcher节点.当节点发生变化的时候,返回变化类型
     * 一个节点可以注册多个监听器对象
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void watcher4() throws KeeperException, InterruptedException {
        //使用自定义的watcher监听
        zooKeeper.exists("/watcher", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println(1);
                System.out.println("path:" + event.getPath());
                System.out.println("type:" + event.getType());
            }
        });
        zooKeeper.exists("/watcher", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println(2);
                System.out.println("path:" + event.getPath());
                System.out.println("type:" + event.getType());
            }
        });
        Thread.sleep(10000);
    }

创建zookeeper连接对象的时候注册watcher

public class ZKConnectionWatcher implements Watcher{ //实现watcher接口
    private static CountDownLatch countDownLatch = new CountDownLatch(1);
    private static ZooKeeper zooKeeper;

    public static void main(String[] args) {
        try {
            zooKeeper = new ZooKeeper("118.190.58.27:2181", 5000, new ZKConnectionWatcher());
            //阻塞线程,等待连接
            countDownLatch.await();
            Thread.sleep(1000);
            zooKeeper.close();
            }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void process(WatchedEvent event) {
        try {
            //事件状态
            if(event.getType() == Event.EventType.None){
                if (event.getState() == Event.KeeperState.SyncConnected){
                    System.out.println("连接创建成功!");
                }else if (event.getState() == Event.KeeperState.Disconnected){
                    System.out.println("断开连接");
                }else if (event.getState() == Event.KeeperState.Expired){
                    System.out.println("会话超时!");
                }else if(event.getState() == Event.KeeperState.AuthFailed){
                    System.out.println("认证失败!!");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

六.zookeeper的应用场景

1.zookeeper实现配置中心的管理

  • 1.zookeeper中创建config中的信息
  • 2.java代码连接zookeeper去读取数据
  • 3.watcher机制去监视这些配置数据,如果发生变化,则重新读取数据
/**
 * @author gl
 * @time 2020-06-15 21:14
 * @function : zookeeper作为配置中心,javaapi读取zookeeper中的节点信息
 * 并且通过监听,一旦节点数据发生改变,则重新读取数据
 * @step :
 */
public class MyConfigCenter implements Watcher {
    private static CountDownLatch countDownLatch = new CountDownLatch(1);
    private static final String ip = "118.190.58.27:2181";
    private ZooKeeper zooKeeper;

    private String url;
    private String username;
    private String password;

    //创建对象的时候,就执行初始化方法
    public MyConfigCenter(){
        initValue();
    }

    /**
     * 初始化发方法
     */
    public void initValue(){
        try {
            zooKeeper = new ZooKeeper(ip, 5000, this);
            //阻塞线程,等待连接创建的成功
            countDownLatch.await();
            this.url = new String(zooKeeper.getData("/config/url",this,null));
            this.username = new String(zooKeeper.getData("/config/username",this,null));
            this.password = new String(zooKeeper.getData("/config/password",this,null));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 监听
     * @param event
     */
    @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 ZKConnectionWatcher());
                }else if(event.getState() == Event.KeeperState.AuthFailed){
                    System.out.println("认证失败!!");
                }
                //当节点变化时,重新读取配置信息
            }else if (event.getType() == Event.EventType.NodeDataChanged){
                initValue();;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public String getUrl() {
        return url;
    }

    public MyConfigCenter setUrl(String url) {
        this.url = url;
        return this;
    }

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public MyConfigCenter setPassword(String password) {
        this.password = password;
        return this;
    }
}

2.zookeeper实现分布式id

  • 1.利用javaAPi的create方法,创建一个临时有序节点,则zookeeper会为我们分配节点的节点序号,使用这个节点序号做为分布式id

3.zookeeper实现分布式锁

  • 1.利用zookeeper的临时有序节点
  • 2.监听上一个节点的状态
  • 3.如果锁处于第一个位置则获取到锁
  • 4.删除节点则释放锁
/**
 * @author gl
 * @time 2020-06-15 21:48
 * @function :利用zookeeper实现分布式锁
 * @step :
 */
public class MyLock {
    //zookeeper连接的IP地址
    private static final String IP = "118.190.58.27:2181";
    //计数对象
    CountDownLatch countDownLatch = new CountDownLatch(1);
    //zookeeper的配置信息
    ZooKeeper zooKeeper;
    private static final String LOCK_ROOT_PATH = "/locks";
    private static final String LOCK_ROOT_NAME = "Lock_";
    private String lockPath ;

    /**
     * 构造方法,初始化zookeeper连接
     */
    public MyLock(){
        try {
            zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getType() == Event.EventType.None){
                        if (event.getState() == Event.KeeperState.SyncConnected){
                            System.out.println("连接成功!");
                            countDownLatch.countDown();
                        }
                    }
                }
            });
            countDownLatch.await();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

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

    //监视器对象,监视上一个节点是否被删除
    Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            if(event.getType() == Event.EventType.NodeDeleted){
               synchronized (this){
                   notifyAll();
               }
            }
        }
    };

    //尝试获取锁
    private void attemptLock() throws Exception{
        //获取locks节点下的所有子节点
        List<String> list = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
        //对子节点排序
        Collections.sort(list);
        // 节点名称:  /Locks/lock_0000000001
        int index = list.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
        //如果当前路劲在第一位,则我们直接获取到锁
        if(index == 0){
            return;
        }else {
            //否则监听上一个节点信息
            //上一个节点的路径
            String path = list.get(index -1);
            //监听上一个节点的状态
            Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + path, watcher);
            //如果上一个节点状态为null,表示上一个节点已经删除,则我们重新获取锁
            if(stat == null){
                attemptLock();
            }else {
                //否则使线程等待
                synchronized (watcher){
                    watcher.wait();
                    attemptLock();
                }
            }

        }

    }

    //创建锁节点
    private void createLock() throws Exception{
        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_ROOT_NAME, new byte[0],
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("节点创建成功!节点路径:" + lockPath);
    }

    //锁释放
    public void releaseLock() throws Exception{
        zooKeeper.delete(lockPath,-1);
        zooKeeper.close();
    }

    //验证创建锁节点方法
    public static void main(String[] args) throws Exception {
        MyLock myLock = new MyLock();
        myLock.createLock();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值