zookeeper分布式锁案例
文档
- linux安装java -centos安装java -linux配置java环境变量
- zookeeper单机安装
- zookeeper集群安装
- zookeeper客户端命令行操作、节点类型及监听器
- zookeeper集群写数据原理
- java操作zookeeper
手写分布式锁案例
原理
-
线程获取锁时,在
/locks
节点下创建临时有序号节点,需要注意的是,有序号的节点序号是递增的create -e -s /locks/seq-
同时获取
/locks
节点的子节点列表,并对子节点列表进行排序,并获取当前节点的索引 -
如果当前节点是索引最小的节点,直接返回,表示获取到锁
-
如果当前节点不是索引最小的节点,设置监听,监听前一个索引节点的值的变化,同时线程等待
-
实际上,以上两步已经将获取锁的顺序设置好了
-
获取到锁的线程执行完之后,删除节点,表示释放锁。此时将触发下一个节点的监听回调,唤醒等待的线程
-
重复步骤5,直到所有的锁都释放
java代码实现分布式锁
-
分布式锁示例代码
DistributedLock.java
package xin.yangshuai.zookeeper01.case2; import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; public class DistributedLock { // 注意:逗号左右不能有空格 private String connectString = "192.168.145.132:2181,192.168.145.133:2181,192.168.145.134:2181"; // 2000毫秒 private int sessionTimeout = 2000; private ZooKeeper zkClient; private CountDownLatch countDownLatch = new CountDownLatch(1); private CountDownLatch waitLatch = new CountDownLatch(1); private String currentMode; private String listeningNode; public DistributedLock() throws IOException, InterruptedException, KeeperException { // 获取连接 zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() { @Override public void process(WatchedEvent watchedEvent) { // 初始化时,会执行一次 System.out.println(watchedEvent.getPath() + "----------------------------"); // 启动时会触发,表示已经连接上zookeeper了,放行 if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { countDownLatch.countDown(); } // 触发监听,监听的节点被删除了,放行 if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(listeningNode)) { waitLatch.countDown(); } } }); countDownLatch.await(); // 已经连接上zookeeper后,继续执行 // 判断父节点/locks是否存在 Stat stat = zkClient.exists("/locks", false); if (stat == null) { // 创建父节点/locks zkClient.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } // 获取锁 public void lock() throws KeeperException, InterruptedException { // 创建临时有序号节点 currentMode = zkClient.create("/locks/seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); Thread.sleep(10); // 获取/locks节点的子节点列表 List<String> children = zkClient.getChildren("/locks", false); if (children.size() == 1) { // 只有一个节点,直接获取锁,返回 return; } Collections.sort(children); // 获取节点名称,seq-00000000 String thisNode = currentMode.substring("/locks/".length()); // 获取节点在集合中的位置 int index = children.indexOf(thisNode); if (index == 0) { // 是第一个节点,直接获取锁,返回 return; } // 获取前一个索引的节点 listeningNode = "/locks/" + children.get(index - 1); // 监听前一个节点的值的变化 zkClient.getData(listeningNode, true, null); // 线程等待,直到上一个节点释放锁(被删除)时,将触发监听回调,会设置此处放行 System.out.println(Thread.currentThread().getName() + " 未获取到锁,等待"); waitLatch.await(); // 上一个节点释放锁,继续执行,表示获取到锁 } // 解锁 public void unlock() throws KeeperException, InterruptedException { // 删除节点,表示释放锁 zkClient.delete(currentMode, -1); } }
-
测试方法示例代码
DistributedLockTest.java
package xin.yangshuai.zookeeper01.case2; import org.apache.zookeeper.KeeperException; import java.io.IOException; public class DistributedLockTest { public static void main(String[] args) throws InterruptedException, IOException, KeeperException { DistributedLock lock1 = new DistributedLock(); DistributedLock lock2 = new DistributedLock(); new Thread(new Runnable() { @Override public void run() { try { lock1.lock(); System.out.println(Thread.currentThread().getName() + " 获取到锁"); Thread.sleep(5000); lock1.unlock(); System.out.println(Thread.currentThread().getName() + " 释放锁"); } catch (KeeperException | InterruptedException e) { e.printStackTrace(); } } }, "线程1").start(); new Thread(new Runnable() { @Override public void run() { try { lock2.lock(); System.out.println(Thread.currentThread().getName() + " 获取到锁"); Thread.sleep(5000); lock2.unlock(); System.out.println(Thread.currentThread().getName() + " 释放锁"); } catch (KeeperException | InterruptedException e) { e.printStackTrace(); } } }, "线程2").start(); } }
-
仅供参考
Curator框架实现分布式锁案例
官网地址:https://curator.apache.org/docs/about
java代码示例
-
测试方法示例代码
CuratorLockTest.java
package xin.yangshuai.zookeeper01.case3; 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; public class CuratorLockTest { // 注意:逗号左右不能有空格 private static String connectString = "192.168.145.132:2181,192.168.145.133:2181,192.168.145.134:2181"; // 2000毫秒 private static int sessionTimeout = 2000; public static void main(String[] args) { // 创建分布式锁1 InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks"); // 创建分布式锁2 InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks"); new Thread(new Runnable() { @Override public void run() { try { lock1.acquire(); System.out.println("线程1 获取到锁"); lock1.acquire(); System.out.println("线程1 再次获取到锁"); Thread.sleep(5000); lock1.release(); System.out.println("线程1 释放锁"); lock1.release(); System.out.println("线程1 再次释放锁"); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { lock2.acquire(); System.out.println("线程2 获取到锁"); lock2.acquire(); System.out.println("线程2 再次获取到锁"); Thread.sleep(5000); lock2.release(); System.out.println("线程2 释放锁"); lock2.release(); System.out.println("线程2 再次释放锁"); } catch (Exception e) { e.printStackTrace(); } } }).start(); } // 连接zookeeper private static CuratorFramework getCuratorFramework() { ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(connectString) .connectionTimeoutMs(sessionTimeout) .sessionTimeoutMs(sessionTimeout) .retryPolicy(policy).build(); // 启动客户端 client.start(); System.out.println("zookeeper客户端启动成功"); return client; } }
-
仅供参考
参考资料
- 尚硅谷