- 分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同 工具ZooKeeper,当然也有着标准的实现方式。
- 设计思路
- 每个客户端往
/Locks下创建临时有序节点/Locks/Lock_00000x
- 客户端取得
/Locks下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功
- 如果自己的锁节点不在第一位,则监听自己前一位的锁节点。例如,自己锁节点 Lock 000000001,则监听前一个锁节点Lock 000000000
- 监听客户端重新执行第2步逻辑,判断自己是否获得了锁
- 获取到锁之后,就可以执行自己的操作。执行完之后,删除自己的锁节点。
1. 实验代码
public class DistributedLock {
private String ip = null;
private static Integer timeOut = 5000;
private final static CountDownLatch countDownLatch = new CountDownLatch(1);
private final static Logger log = Logger.getLogger(DistributedLock.class);
private ZooKeeper zooKeeper = null;
private static final String LOCK_ROOT_PATH = "/Locks";
private static final String LOCK_NODE_NAME = "Lock_";
String lockPath;
public DistributedLock() {
}
public DistributedLock(String ip) {
this(ip, timeOut);
}
public DistributedLock(String ip, Integer timeOut) {
this.ip = ip;
DistributedLock.timeOut = timeOut;
try {
initZK(ip, timeOut);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private void initZK(String ip, Integer timeOut) throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(ip, timeOut, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
log.info("连接成功");
countDownLatch.countDown();
}
}
});
countDownLatch.await();
}
public void release() {
try {
zooKeeper.delete(lockPath, -1);
zooKeeper.close();
log.info("已释放" + this.lockPath);
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
}
public void acquire() {
try {
createLock();
attemptLock();
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
private void createLock() throws KeeperException, InterruptedException {
if (zooKeeper.exists(LOCK_ROOT_PATH, true) == null) {
zooKeeper.create(LOCK_ROOT_PATH, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
log.info("创建父节点: " + LOCK_ROOT_PATH + " 成功");
}
lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
log.info("创建节点成功:" + lockPath);
}
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
synchronized (this) {
notifyAll();
}
}
}
};
private void attemptLock() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren(LOCK_ROOT_PATH, null);
Collections.sort(children);
int index = children.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
if (index == 0) {
log.info("获取锁成功");
return;
}
String lastPath = children.get(index - 1);
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + lastPath, watcher);
if (stat == null) {
attemptLock();
return;
}
synchronized (watcher) {
watcher.wait();
}
attemptLock();
}
}
public class DistributedLockTest {
public static void main(String[] args) {
DistributedLock lock = new DistributedLock("192.168.233.133:2181");
lock.acquire();
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.release();
}
}