2023-01-12
节点
4.4. 子节点实现的锁
在生产环境中,使用子节点做分布式锁的实现的场景是最常见的,包括阻塞型的锁和非阻塞型的锁。因此,设计子节点实现的锁的父类,提供公共的处理逻辑。将阻塞型和非阻塞型的实现不同的逻辑分别在子类中实现即可。
package com.qianfeng.lock.childNodeLock;
import com.qianfeng.lock.ZkLocker;
import com.qianfeng.lock.ZkLockerBase;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import java.util.Collections;
import java.util.List;
/**
* @author 千锋大数据教研院 - 章鱼哥
* @company 北京千锋互联科技有限公司
*/
public abstract class ZkChildNodeLockerBase extends ZkLockerBase implements ZkLocker {
/**
* 创建的子节点锁的全路径
*/
protected String nodeFullPath;
public ZkChildNodeLockerBase(String connectString, String lockName) {
super(connectString, lockName);
createLockNode();
}
public ZkChildNodeLockerBase(String lockName) {
super();
this.lockName = lockName;
createLockNode();
}
// 创建锁节点
public void createLockNode() {
// 1. 判断节点是否存在
if (!exists(getLockName())) {
// 3. 创建锁节点
createNode(getLockName(), CreateMode.CONTAINER);
}
}
@Override
public boolean lock() {
// 1. 在锁节点下创建子节点
this.nodeFullPath = createNode(getLockName() + "/child-", CreateMode.EPHEMERAL_SEQUENTIAL);
// 2. 判断是否可以成功上锁
return canLock();
}
@Override
public boolean unlock() {
return deleteNode(this.nodeFullPath);
}
@Override
public boolean exists() {
// 获取当前锁节点下的子节点数量,如果大于0,说明有锁存在
try {
Listchildren = getZkCli().getChildren(getLockName(), false);
return children.size() > 0;
} catch (KeeperException | InterruptedException ignored) {
}
return false;
}
protected abstract boolean canLock();
/**
* 获取当前创建的子节点之前的节点(根据序号)
* @return 之前的节点
*/
protected String getPreviousNode() {
// 定义变量,记录上一个节点名称
String previousNodeName = null;
try {
// 1. 获取锁节点下所有的子节点
Listchildren = getZkCli().getChildren(getLockName(), false);
// 2. 对所有的节点进行名字排序
Collections.sort(children);
// 3. 获取当前创建的子节点名称
String childNodeName = this.nodeFullPath.substring(getLockName().length() + 1);
// 4. 遍历所有的节点,进行名称的比对
for (String child : children) {
if (child.equals(childNodeName)) {
break;
}
previousNodeName = child;
}
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
return previousNodeName;
}
}
4.5. 子节点实现的非阻塞型锁
package com.qianfeng.lock.childNodeLock;
/**
* @author 千锋大数据教研院 - 章鱼哥
* @company 北京千锋互联科技有限公司
*/
public class ZkChildNodeNoneBlockingLocker extends ZkChildNodeLockerBase {
public ZkChildNodeNoneBlockingLocker(String connectString, String lockName) {
super(connectString, lockName);
}
public ZkChildNodeNoneBlockingLocker(String lockName) {
super(lockName);
}
@Override
protected boolean canLock() {
// 1. 判断是否有之前的节点
String previousNodeName = getPreviousNode();
// 2. 如果不存在之前的节点,说明上锁成功
if (previousNodeName == null) {
return true;
}
// 3. 如果存在之前的节点,说明上锁失败,删除自己创建的子节点即可
deleteNode(this.nodeFullPath);
return false;
}
}
4.6. 子节点实现的阻塞型锁
A程序在进行上锁的时候,发现已经被其他的程序获取到锁了,自己需要等待其他的程序释放锁。基本的实现逻辑就是自己监控已经创建好的节点,发现这个节点消失的时候,自己去创建节点。但是为什么没有用节点作为锁来实现阻塞型的锁呢?是因为这里有“羊群效应”。
假如有100个程序来获取锁,结果发现锁已经被其他的程序注册了,于是这100个程序都去监听这个节点。一旦这个节点被删除,这100个程序都可以获取到状态的变更,会同时请求ZooKeeper创建节点。这样会带来瞬时的资源占用剧增,严重的甚至可能导致服务器宕机。因此,我们在实现阻塞型锁的时候,使用的是有序的子节点来实现的,每一个程序监控比自己编号小的节点即可。详情可参考3.2.章节的内容。
package com.qianfeng.lock.childNodeLock;
import org.apache.zookeeper.AddWatchMode;
import org.apache.zookeeper.KeeperException;
import java.util.concurrent.CountDownLatch;
/**
* @author 千锋大数据教研院 - 章鱼哥
* @company 北京千锋互联科技有限公司
*/
public class ZkChildNodeBlockingLocker extends ZkChildNodeLockerBase {
// 等待释放信号
private CountDownLatch latch = new CountDownLatch(1);
public ZkChildNodeBlockingLocker(String connectString, String lockName) {
super(connectString, lockName);
}
public ZkChildNodeBlockingLocker(String lockName) {
super(lockName);
}
@Override
protected boolean canLock() {
// 获取自己创建的子节点之前序号的节点
String previousNode = getPreviousNode();
// 如果之前节点不存在,说明已经上锁成功
if (previousNode == null) {
return true;
}
// 如果有比当前创建的节点序号更小的节点,说明上锁失败,自己阻塞,监听之前的节点即可
try {
getZkCli().addWatch(getLockName() + "/" + previousNode, event -> {
if (event.getType().equals(Event.EventType.NodeDeleted)) {
// 说明之前节点被删除掉了
latch.countDown();
}
}, AddWatchMode.PERSISTENT);
// 等待信号
latch.await();
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
// 自己成为了最小的节点,上锁成功
return true;
}
}
上一篇:分布式锁的实现(一)
下一篇:ZooKeeper的选举制度
开班时间:2021-04-12(深圳)
开班盛况开班时间:2021-05-17(北京)
开班盛况开班时间:2021-03-22(杭州)
开班盛况开班时间:2021-04-26(北京)
开班盛况开班时间:2021-05-10(北京)
开班盛况开班时间:2021-02-22(北京)
开班盛况开班时间:2021-07-12(北京)
预约报名开班时间:2020-09-21(上海)
开班盛况开班时间:2021-07-12(北京)
预约报名开班时间:2019-07-22(北京)
开班盛况Copyright 2011-2023 北京千锋互联科技有限公司 .All Right 京ICP备12003911号-5 京公网安备 11010802035720号