分布式锁
- mysql
- redis 的 setnx
- redission
- redLock
- zookeeper
- curator
redis的分布式锁可用性更高,但是分布式不友好。一般单机的redis实现分布式锁性能就够用。
如果非要要求可靠性,可以选择zk,只是zk是cp的,性能要差一点。
Reids分布式锁
手写 zk 分布式锁
zk 实现分布式锁,是依赖 zk 的临时有序节点。
多个线程在 rootPath 下面按顺序创建节点。
- 首先有持久节点lock
- 每个请求获取锁的时候会在持久节点lock下创建一个临时节点
- 创建临时节点之后会判断自己是不是最小的节点,如果是,代表获取到锁。如果不是,则watch上一个节点。
- 获取锁的节点删除临时节点时,下一个节点就能获取到锁。这样能避免多线程竞争,类似于排队等锁。
java
@Slf4j
public class ZkLockUtil implements AutoCloseable {
private final ZooKeeper zooKeeper;
private String zNode;
public ZkLockUtil() {
ZooKeeper zooKeeperClient = null;
try {
zooKeeperClient = new ZooKeeper("localhost:2181", 100000, null);
} catch (IOException e) {
e.printStackTrace();
}
this.zooKeeper = zooKeeperClient;
}
@Override
public void close() throws Exception {
zooKeeper.delete(zNode, -1);
}
public boolean getLock(String businessCode) {
try {
//判断根节点
judgeRootPath(businessCode);
//创建临时有序节点,/order/order_0000001
zNode = zooKeeper.create("/" + businessCode + "/" + "_", businessCode.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
//序号最小的获取到锁
List<String> childrens = zooKeeper.getChildren("/" + businessCode, false);
Collections.sort(childrens);
String minZnode = childrens.get(0);
//创建的节点是最小节点,就获取锁
if (zNode.endsWith(minZnode)) {
return true;
}
//监听上一个节点的删除事件
String preNode = minZnode;
CountDownLatch countDownLatch = new CountDownLatch(1);
for (String cruZnode : childrens) {
if (zNode.endsWith(cruZnode)) {
//watch preZNode,当 preZNode 删除的时候触发
zooKeeper.exists("/" + businessCode + "/" + preNode, watchedEvent -> {
countDownLatch.countDown();
});
} else {
preNode = cruZnode;
}
}
//阻塞自身,让出锁
synchronized (this) {
countDownLatch.await();
}
//当被唤醒的时候,说明上个节点被删除了,此时获取到锁
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private void judgeRootPath(String businessCode) {
try {
//判断根节点是否存在
Stat exists = zooKeeper.exists("/" + businessCode, false);
//节点不存在,首次创建
if (Objects.isNull(exists)) {
//创建根节点目录
//永久节点
zooKeeper.create("/" + businessCode, businessCode.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException | InterruptedException e) {
log.error("zk judge root path error:{}",e.getMessage());
}
}
}
Curator
Curator 封装了 zk 实现分布式锁的细节。
java
@SneakyThrows
public void create(int num) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy);
client.start();
InterProcessMutex lock = new InterProcessMutex(client, "/" + ORDER_KEY);
try {
if (lock.acquire(30, TimeUnit.SECONDS)) {
log.info("{} get lock", num);
Thread.sleep(600);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
log.info("{} release lock", num);
lock.release();
client.close();
}
}