基于Redis的分布式锁实现
基于ZooKeeper的分布式锁实现
原理介绍
别人写得很好,请直接参考 Zookeeper实现分布式锁
基于ZK的分布式锁,原理主要是依赖ZK的临时自增节点,其会在client断开连接时自动删除当前client所持有的节点,并且在获取的时候自增序列.
然后在利用watch机制,让后继节点监听前驱,当发生变化时,处理对应事件.
watch机制
在zk中,可以设置一些监听,来监听节点的一些变化操作,做出响应。
绑定监听:getData、exists、getChildren
触发监听:create、delete、setData
查操作可以绑定监听,增、删、改可以绑定监听,且监听是一次性的,在触发一次会消失,可以循环绑定来达到一直监听的效果。
准备工作
搭建ZooKeeper服务集群.
# 准备zookeeper
docker pull zookeeper:zookeeper@3.6
# 根据官方的stack.yml文件 https://hub.docker.com/_/zookeeper 启动3个zk容器
docker stack deploy -c stack.yml zookeeper
目标
- 实现分布式锁
- 并发争用的冲突
- 公平锁
- 非公平锁
- 当前锁持有者故障,不影响其他等待者
- 性能优化
代码实现
emm先贴一下,之后再改,见谅.
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class ZKLock implements Lock {
public static class MyWatcher implements Watcher {
@Override
public void process(WatchedEvent event) {
System.out.println(event);
}
}
public static class MyCallBack implements AsyncCallback.StringCallback {
private final CountDownLatch receiveCDL;
public MyCallBack(CountDownLatch receiveCDL) {
this.receiveCDL = receiveCDL;
}
@Override
public void processResult(int rc, String path, Object ctx, String name) {
System.out.println("创建结果:rc=" + rc + ",path=" + path + ",ctx=" + ctx + ",name=" + name);
switch (rc) {
case 0:
System.out.println("节点创建成功:" + name);
break;
case -4:
System.out.println("客户端与服务端连接已断开");
break;
case -110:
System.out.println("指定节点已存在");
break;
case -112:
System.out.println("会话已过期");
break;
default:
System.out.println("服务端响应码" + rc + "未知");
break;
}
receiveCDL.countDown();
}
}
private final ZooKeeper zk;
private final String zNodeName;
private String lockName;
public ZKLock(ZooKeeper zooKeeper, String name) {
this.zk = zooKeeper;
this.zNodeName = "/" + name;
}
@Override
public void lock() {
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
try {
final Stat exists = zk.exists(zNodeName, false);
if (null == exists) {
synchronized (ZKLock.class) {
final Stat exists1 = zk.exists(zNodeName, false);
if (null == exists1) {
System.out.println("create " + zNodeName);
final CountDownLatch countDownLatch = new CountDownLatch(1);
final MyCallBack myCallBack = new MyCallBack(countDownLatch);
zk.create(zNodeName, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, myCallBack, "");
countDownLatch.await();
// 监听目录变化
// zk.addWatch(zNodeName, event -> {
// System.out.println("dis:" + event.toString());
// }, AddWatchMode.PERSISTENT);
}
}
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
lockName = zk.create(zNodeName + "/", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
final String[] split = lockName.split("/");
final int seq = Integer.parseInt(split[split.length - 1]);
if (0 >= seq) {
System.out.println("new lock: " + lockName + ", don't wait.");
return true;
}
// 监听上一个节点
final String lastLockName = String.format("%s/%010d", zNodeName, seq - 1);
// System.out.println("lastLockName: " + lastLockName);
final Stat lastExists = zk.exists(lastLockName, false);
if (null != lastExists) {
// 监听前一个节点的变化
final CountDownLatch downLatch = new CountDownLatch(1);
zk.addWatch(lastLockName, event -> {
// System.out.println("seq:" + event.toString());
switch (event.getType()) {
case NodeDeleted:
downLatch.countDown();
break;
}
}, AddWatchMode.PERSISTENT);
System.out.println("new lock: " + lockName + ", waiting " + lastLockName + " unlock.");
downLatch.await();
}
return true;
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
try {
zk.delete(lockName, -1);
System.out.println("unlock:" + lockName);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
public void print() {
final List<String> ephemerals;
try {
ephemerals = zk.getEphemerals(zNodeName);
System.out.println(ephemerals);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public Condition newCondition() {
return null;
}
public static void main(String[] args) throws Exception {
final String hosts = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
ZooKeeper zk = new ZooKeeper(hosts, 5000, new MyWatcher());
System.out.println(zk.getState());
int counter = 10;
final ExecutorService es = Executors.newFixedThreadPool(5);
final CountDownLatch downLatch = new CountDownLatch(counter);
// 通过线程池模拟并发
for (int i = 0; i < counter; i++) {
es.submit(() -> {
final ZKLock zkLock = new ZKLock(zk, "dis");
zkLock.tryLock();
try {
// 暂停一定时间,模拟耗时操作
final int sleepTime = 2000;
System.out.println("Thread-" + Thread.currentThread().getId() + ", sleep:" + sleepTime);
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
zkLock.unlock();
downLatch.countDown();
});
}
downLatch.await();
System.out.println("completed.");
}
}