文章目录

  • 一、搭建Zookeeper
  • 1、下载
  • 2、安装启动
  • 二、Zookeeper分布式锁简述
  • 三、curator客户端使用分布式锁
  • 1、curator概述
  • 2、curator使用
  • 四、测试效果
  • 1、几个常见命令
  • 1)ls
  • 2)stat
  • 3)get
  • 4)set
  • 2、加锁解锁效果
  • 3、竞争锁效果


一、搭建Zookeeper

官网地址:https://zookeeper.apache.org/

1、下载

官方下载地址:https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.8.0/apache-zookeeper-3.8.0-bin.tar.gz

zookeeper 国内tar包下载_apache

2、安装启动

1> 解压

tar -zxvf apache-zookeeper-3.8.0-bin.tar.gz

2> 创建数据存放目录

cd apache-zookeeper-3.8.0-bin
mkdir data

3> 配置文件修改(conf目录下)

cd ../conf

cp zoo_sample.cfg zoo.cfg

# 修改配置文件
vi zoo.cfg
dataDir=/Users/xxx/software/zookeeper/apache-zookeeper-3.8.0-bin/data

zookeeper 国内tar包下载_apache_02


4> 启动

cd ../bin
./zkServer.sh start

5> 查看状态

./zkServer.sh status

zookeeper 国内tar包下载_apache_03


这里Zookeeper是以standalone单机模式运行;

Zookeeper集群搭建参考文章:。

二、Zookeeper分布式锁简述

Zookeeper实现排他锁的设计思路如下:

  • zk用/lock节点作为分布式锁,当不同的客户端到zk竞争这把锁的时候,zk会按顺序给不同的客户端创建一个临时子节点,挂在作为分布式锁的节点下面。
  • 假设第一个来到的客户端为A,第二个来到的是B,分布式锁节点下挂的第一个节点就是A(/lock/_c_A),B(/lock/_c_B)紧跟着A,且B会监听着A的生命状态;
  • 这里B会先获取到/lock路径下所有的节点,发现自己的锁节点(/lock/_c_B)不在第一位,进而监听自己前一位的锁节点(/lock/_c_A)。
  • 当A释放锁后A节点会被删除;B监听到A被删除,B可以尝试获得分布式锁了。
  • 具体体现为:客户端B获取/lock下的所有子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表取锁成功。
  • 如果还有C节点、D节点,他们都只会监听他们前一个节点,即:C监听B、D监听C。

三、curator客户端使用分布式锁

Curator提供的InterProcessMutex是分布式锁的实现。acquire()方法用于获取锁,release()方法用于释放锁。

1、curator概述

github: https://github.com/apache/curator

官方地址: https://curator.apache.org/

Apache Curator是Apache ZooKeeper(一种分布式协调服务)的Java/JVM客户端库。它包括一个高级API框架和实用程序,使Apache ZooKeeper的使用更加容易和可靠。

zookeeper 国内tar包下载_分布式锁_04

2、curator使用

引入curator的maven依赖,或者直接在curator源码中运行下列demo;

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>5.4.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.4.0</version>
</dependency>

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.8.0</version>
    <exclusions>
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

zookeeper 国内tar包下载_apache_05

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * @author Saint
 */
public class LockTest {
    public static void main(String[] args) {
        //重试策略,定义初试时间3s,重试3次
        ExponentialBackoffRetry exponentialBackoffRetry = new ExponentialBackoffRetry(3000, 3);

        //初始化客户端
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")
                .sessionTimeoutMs(3000)
                .connectionTimeoutMs(3000)
                .retryPolicy(exponentialBackoffRetry)
                .build();
        // start()开始连接,没有此会报错
        client.start();
        //利用zookeeper的类似于文件系统的特性进行加锁  第二个参数指定锁的路径
        InterProcessMutex interProcessMutex = new InterProcessMutex(client, "/lock");

        try {
            //加锁
            interProcessMutex.acquire();
            System.out.println(Thread.currentThread().getName() + "获取锁成功");
            Thread.sleep(60_000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                //释放锁
                interProcessMutex.release();
                System.out.println(Thread.currentThread().getName() + "释放锁成功");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

四、测试效果

在测试代码中,加锁成功之后线程会sleep 60s,这期间我们连上zookeeper可以看下效果;

1、几个常见命令

1)ls

ls命令:查看目录下的节点信息

ls [-s] [-w] [-R] path
  • -s:显示节点详情,包括状态信息
  • -w:添加一个watch监视器
  • -R:列举出节点的级联节点
  • path:显示某目录下节点/文件

2)stat

stat命令:查看节点状态。

stat [-w] path
  • -w:添加watch
  • path:查看某目录下节点的状态

3)get

get命令:获取节点/文件内容

get [-s] [-w] path
  • -s:查看节点数据以及节点状态信息
  • -w:添加一个watch,节点数据变更时,会通知客户端(通知是一次性的)。

4)set

set命令:修改节点内容

set [-s] [-v version] path data
  • -s:更新节点数据并显示节点状态信息
  • -v 指定数据版本号,如果指定的数据版本号和数据当前版本号不一致,则更新失败。

2、加锁解锁效果

1> 使用zkCli.sh 链接zookeeper

zookeeper 国内tar包下载_分布式锁_06


2> 加锁前后、释放锁后效果:

  • 加锁后,/lock路径下会多一个临时节点;临时节点的内容为客户端IP
  • 释放锁后,/lock路径的临时节点会被删除。

3、竞争锁效果

启动三次测试类 模拟多个并发的效果,idea中进行如下设置:

zookeeper 国内tar包下载_分布式锁_07

开启三次这个测试类之后,zookeeper的/lock路径下会有三个节点:

zookeeper 国内tar包下载_apache_08

后启动的依赖于前一个启动的测试类释放分布式锁之后,才能获取到分布式锁。