Zookeeper特性与分布式锁(含安装与集群搭建)

前言:本文为原创 若有错误欢迎评论!

一.安装

1.linux安装(非Docker)

  1. 下载zookeeper-3.4.14.tar.gz(https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/)

    链接:https://pan.baidu.com/s/1DrmzdxgCCGjBHUwTKBehPQ
    提取码:p959
    复制这段内容后打开百度网盘手机App,操作更方便哦

  2. 上传到虚拟机并解压 解压后 进入conf 修改为zoo.cfg

  3. 在zookeeper下新建一个data目录 将zoo.cfg的dataDir修改为该data目录底下的地址

  4. 进入bin目录启动: ./zkServer.sh start

  5. 开放端口:2181

2.linux安装(Docker)

  1. 拉取zk镜像

    docker pull zookeeper:3.5

  2. 创建容器

    docker create --name zk -p 2181:2181 zookeeper:3.5

  3. 启动容器

    docker start zk

3.windows管控台

  1. 下载

    链接:https://pan.baidu.com/s/14ogQivPWgLe0bPqcrJN-sQ
    提取码:3phj
    复制这段内容后打开百度网盘手机App,操作更方便哦

  2. 运行:ZooInspector\build\start.bat

  3. 点击左上角运行 并改为正确的地址 然后点击"OK"

二. Zookeeper基础

1.常用命令

  • 创建节点

create [-s] [-e] path data acl
( -s表示创建顺序节点 -e表示创建临时节点 data表示创建的节点的数据内容)

  • 查看节点

ls path (获取节点的子节点)
get path (获取节点的数据)
stat path (查看节点状态)
ls2 path (获取节点的子节点以及当前节点的状态)

  • 修改节点

set path data [version]
注意:version必须等于当前的dataversion 相当于一个乐观锁 每次修改一次version会自增一次 所以多个线程可以同时操作 但由于version被一个线程操作后会自增 而其他线程用的version还是旧的version就不会操作成功

  • 删除节点

delete path [version] :如果此时该节点有子节点,则不允许删除
rmr path :递归删除整个节点
注意:所有的path必须从跟根路径开始 都是‘/path‘的形式

2.session机制

  1. 用于客户端与服务端之间的连接,可设置超时时间,通过心跳包的机制(客户端向服务端ping包请求)检查心 跳结束,session就过期
  2. session过期的时候,该session创建的所有临时节点都会被抛弃
    注意:使用close直接关闭客户端的话 链接直接断掉 子节点也直接删除 但是客户端下线的话在超时时间内上线不会删除临时子节点

3.watcher机制

  1. 监视单一节点(对子节点无效 增删改自己的数据会触发):

get path watch
stat path watch

  1. 监视子节点(只有字典点增删有效 修改子节点数据不会触发):

ls path watch
ls2 path watch

  • 注意:所有的watch都是一次性的 触发一次之后就会失效

二.Zookeeper集群

  • 端口:2181+2888 3888(后两个用于自己配置的集群之间通信)

非Docker搭建

1.先给一个虚拟机安装好zookeeper

2.修改配置文件名为zoo.cfg

3.编辑zoo.cfg

修改:
dataDir=/usr/local/linux/zookeeper/data

添加:
server.1=ip1:2888:3888 server.2=ip2:2888:3888 server.3=ip3:2888:3888

4.在自己新建的data目录下新建myid(没有’.'文件的后缀)

5.把zookeeper的解压包拷贝到三个虚拟机

6.编辑每个虚拟机的myid文件 内容均只有一个数字(表示myid) 依次为1、2、3

7.开放2181 2888 3888端口

8.依次启动 ./zkServer start

9.查看启动和节点的状态:./zkServer status (会显示每个节点的Mode)

Docker搭建
1.拉取zk镜像

  • 注意:版本要和服务端引入的zk的版本一致
docker pull zookeeper:3.4 

2.创建容器

docker create --name zk01 --net host \
-e ZOO_MY_ID=1 \
-e ZOO_SERVERS="server.1=192.168.1.7:2888:3888 server.2=192.168.1.18:2888:3888 server.3=192.168.1.19:2888:3888" \
zookeeper:3.4 

docker create --name zk02 --net host \
-e ZOO_MY_ID=2 \
-e ZOO_SERVERS="server.1=192.168.1.7:2888:3888 server.2=192.168.1.18:2888:3888 server.3=192.168.1.19:2888:3888" \
zookeeper:3.4 

docker create --name zk03 --net host \
-e ZOO_MY_ID=3 \
-e ZOO_SERVERS="server.1=192.168.1.7:2888:3888 server.2=192.168.1.18:2888:3888 server.3=192.168.1.19:2888:3888" \
zookeeper:3.4 

3.三个虚拟机分别启动容器

docker start zk01 && docker logs -f zk01 
docker start zk02 && docker logs -f zk02 
docker start zk03 && docker logs -f zk03

三.分布式锁

  • 原因:
    单机的锁没法满足(即本地的每一个线程安全)没法保证多台机子部署的每一个线程安全
  • 原理:
    • zookeeper:通过能否创建子节点来判断 线程是否拿到锁(必须设置为-e 临时节点 因为一旦宕机后会断开session 然后释放掉锁 不然永远不会释放锁)
    • redis:通过是否可以setnx判断 线程是否拿到锁(必须再setex设置时间 不然宕机后无法释放锁)

1.Zookeeper依赖:

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

2.使用:

  1. 定义全局变量
ZooKeeper zooKeeper
CountDowmLatch countDownLatch;
  1. 在构造方法里:
private ZkLock() {
        try {
            //获取zookeeper对象
            zooKeeper = new ZooKeeper("192.1.1.101:2181,192.1.1.102:2181,192.1.1.103:2181", 5000, new ZkWatcher());
            try {
       		     //让线程等待watcher确认连接
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
       } catch (IOException e) {
            e.printStackTrace();
      }
}
  1. 新建一个内部类 implements Watcher 重写process()方法
private class ZkWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            System.out.println("接收到监听事件=====》"+event);
            if (Event.KeeperState.SyncConnected == event.getState()) {
                // 判断是否连接上 然后不让countDownLatch再等待
                countDownLatch.countDown();
            }
        }
}
  1. 定义加锁的方法 使用zookeeper.create()方法创建子节点
    当前线程若是没拿到锁创建失败会抛出异常 在异常中让当前线程睡眠 然后继续尝试拿锁
public void lock(Integer id) {
        String path = "/xdclass-product-lock-" + id;
        //创建临时节点,如果创建成功的话,就表示获取锁,如果失败,则不断尝试
        while(true){
            try {
                    zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            } catch (Exception e) {
                    try {
                        Thread.sleep(5000);
                        continue;
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
            
            }
            break;
        }
 }

5.释放锁 调用zookeeper.delete()

public void unLock(Integer id) {
        String path = "/xdclass-product-lock-" + id;
        try {
            zooKeeper.delete(path,-1);
        } catch (Exception e) {
            e.printStackTrace();
        }
}

注意:存储的节点的名字要有区分性 且zookeeper.create()的path不能包含子路径 只能是一层路径

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值