官网地址:https://zookeeper.apache.org/
官方下载地址:https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.8.0/apache-zookeeper-3.8.0-bin.tar.gz
1> 解压
tar -zxvf apache-zookeeper-3.8.0-bin.tar.gz
2> 创建数据存放目录
cd apache-zookeeper-3.8.0-bin
mkdir data
3> 配置文件修改(conf目录下)
cd ../confcp zoo_sample.cfg zoo.cfg# 修改配置文件
vi zoo.cfg
dataDir=/Users/xxx/software/zookeeper/apache-zookeeper-3.8.0-bin/data
4> 启动
cd ../bin
./zkServer.sh start
5> 查看状态
./zkServer.sh status
这里Zookeeper是以standalone单机模式运行;
Zookeeper集群搭建参考文章:https://saint.blog.csdn.net/article/details/117109245。
Zookeeper实现排他锁的设计思路如下:
zk用/lock
节点作为分布式锁,当不同的客户端到zk竞争这把锁的时候,zk会按顺序给不同的客户端创建一个临时子节点,挂在作为分布式锁的节点下面。
假设第一个来到的客户端为A,第二个来到的是B,分布式锁节点下挂的第一个节点就是A(/lock/_c_A
),B(/lock/_c_B
)紧跟着A,且B会监听着A的生命状态;
/lock
路径下所有的节点,发现自己的锁节点(/lock/_c_B
)不在第一位,进而监听自己前一位的锁节点(/lock/_c_A
)。当A释放锁后A节点会被删除;B监听到A被删除,B可以尝试获得分布式锁了。
具体体现为:客户端B获取/lock
下的所有子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表取锁成功。
如果还有C节点、D节点,他们都只会监听他们前一个节点,即:C监听B、D监听C。
Curator提供的InterProcessMutex是分布式锁的实现。acquire()
方法用于获取锁,release()
方法用于释放锁。
github: https://github.com/apache/curator
官方地址: https://curator.apache.org/
Apache Curator是Apache ZooKeeper(一种分布式协调服务)的Java/JVM客户端库。它包括一个高级API框架和实用程序,使Apache ZooKeeper的使用更加容易和可靠。
引入curator的maven依赖,或者直接在curator源码中运行下列demo;
org.apache.curator curator-recipes 5.4.0 org.apache.zookeeper zookeeper
org.apache.curator curator-framework 5.4.0
org.apache.zookeeper zookeeper 3.8.0 log4j log4j org.slf4j slf4j-log4j12
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可以看下效果;
ls命令:查看目录下的节点信息
ls [-s] [-w] [-R] path
stat命令:查看节点状态。
stat [-w] path
get命令:获取节点/文件内容
get [-s] [-w] path
set命令:修改节点内容
set [-s] [-v version] path data
1> 使用zkCli.sh 链接zookeeper
2> 加锁前后、释放锁后效果:
启动三次测试类 模拟多个并发的效果,idea中进行如下设置:
开启三次这个测试类之后,zookeeper的/lock路径下会有三个节点:
后启动的依赖于前一个启动的测试类释放分布式锁之后,才能获取到分布式锁。