实现思路
实现的分布式锁的最常见方式是使用redis分布式锁(redis分布式锁参考我的专栏——分布式锁),基本原理是在各个服务执行到临界区(造作共享资源的代码块)之前,去redis中尝试添加一个带有当前贡献资源标识的redis数据,如果redis中没有该键值,添加成功,代表加锁成功,如果redis中已经存在该键值,添加失败,则加锁失败。
而zookeeper的原理就完全不一样了:
- 首先,每个客户端会去某个指定的znode(一般情况,一类分布式锁都创建在某一个node path下)下创建临时有序节点,所以每个客户端创建出来的临时节点都会按照序列分配序号如/locks/lock_000000001、/locks/lock_000000002、/locks/lock_000000003.......
- 客户端获取到/lock下的子节点,并进行排序,判断排在最前面的节点是否是自己创建的节点,如果是,那么就获取到锁。
- 如果自己创建的节点并不在第一个,则监听前一个节点
- 前一个节点的客户端执行完毕,释放锁之后,就会触发监听,通知到下一个节点的客户端
- 监听客户端重新执行第2步动作,尝试获取锁
基本实现
本文的demo示例仅作为zookeeper分布式锁的原理分析示例!!!
引入zookeeper自带Java客户端
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
</dependency>
编写示例
package org.leolee.zookeeper.distributedLock;
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;
/**
* @ClassName MyLock
* @Description: 分布式锁
* @Author LeoLee
* @Date 2021/4/14
* @Version V1.0
**/
public class MyLock {
private static final String IP = "127.0.0.1:2181";
CountDownLatch countDownLatch = new CountDownLatch(1);
ZooKeeper zooKeeper;
private static final String LOCK_ROOT_PATH = "/locks";
private static final String LOCK_NODE_NAME = "lock_";
private String lockPath;
//连接zookeeper
public MyLock() {
try {
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState().equals(Event.KeeperState.SyncConnected)) {
System.out.println("connect success");
countDownLatch.countDown();
} else if (watchedEvent.getState().equals(Event.KeeperState.Disconnected)) {
System.out.println("connection break");
} else if (watchedEvent.getState().equals(Event.KeeperState.Expired)) {
System.out.println("session timeout");
} else if (watchedEvent.getState().equals(Event.KeeperState.AuthFailed)) {
System.out.println("auth failed");
}
}
});
countDownLatch.await();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
//监视的前一个节点被删掉后通知
if (watchedEvent.getType().equals(Event.EventType.NodeDeleted)) {
synchronized (watcher) {
watcher.notifyAll();
}
}
}
};
/**
* 功能描述: <br>
* 〈〉获取锁
* @Param: []
* @Return: void
* @Author: LeoLee
* @Date: 2021/4/14 23:00
*/
public void acquireLock() throws KeeperException, InterruptedException {
createLock();
tryLock();
}
private void createLock() throws KeeperException, InterruptedException {
//判断锁根节点是否存在
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_NODE_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("lock node create success, lock path:" + lockPath);
}
private void tryLock() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
//对子节点进行排序
Collections.sort(children);
int index = children.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
if (index == 0) {
System.out.println("try lock success");
return;
} else {
//获取上一个节点
String preNode = children.get(index - 1);
//对上一个节点进行watch
Stat preNodeStat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + preNode, watcher);
if (preNodeStat == null) {
acquireLock();
} else {
synchronized (watcher) {
watcher.wait();
}
}
}
}
/**
* 功能描述: <br>
* 〈〉释放锁
* @Param: []
* @Return: void
* @Author: LeoLee
* @Date: 2021/4/14 23:01
*/
public void releaseLock() throws KeeperException, InterruptedException {
//删除临时有序节点
zooKeeper.delete(lockPath, -1);
System.out.println("lock release");
zooKeeper.close();
}
}