}
}
}
private Map<Long, String> orderedChildren(Watcher watcher) throws KeeperException, InterruptedException {
Map<Long, String> orderedChildren = new TreeMap<>();
List childNames;
childNames = zookeeper.getChildren(dir, watcher);
for (String childName : childNames) {
try {
if (!childName.regionMatches(0, prefix, 0, prefix.length())) {
LOG.warn(“Found child node with improper name: {}”, childName);
continue;
}
String suffix = childName.substring(prefix.length());
Long childId = Long.parseLong(suffix);
orderedChildren.put(childId, childName);
} catch (NumberFormatException e) {
LOG.warn(“Found child node with improper format : {}”, childName, e);
}
}
return orderedChildren;
}
3)、remove方法
public class DistributedQueue {
public byte[] remove() throws NoSuchElementException, KeeperException, InterruptedException {
Map<Long, String> orderedChildren;
while (true) {
try {
//获取所有排好序的子节点
orderedChildren = orderedChildren(null);
} catch (KeeperException.NoNodeException e) {
throw new NoSuchElementException();
}
if (orderedChildren.size() == 0) {
throw new NoSuchElementException();
}
//移除队头节点
for (String headNode : orderedChildren.values()) {
String path = dir + “/” + headNode;
try {
byte[] data = zookeeper.getData(path, false, null);
zookeeper.delete(path, -1);
return data;
} catch (KeeperException.NoNodeException e) {
//另一个客户端已经移除了队头节点,尝试移除下一个节点
}
}
}
}
2、分布式锁
1)、排他锁
排他锁的核心是如何保证当前有且仅有一个事务获取锁,并且锁被释放后,所有正在等待获取锁的事务都能够被通知到
定义锁
通过在ZooKeeper上创建一个子节点来表示一个锁,例如/exclusive_lock/lock
节点就可以被定义为一个锁
获取锁
在需要获取排他锁时,所有的客户端都会试图通过调用create()
接口,在/exclusive_lock
节点下创建临时子节点/exclusive_lock/lock
。ZooKeeper会保证在所有的客户端中,最终只有一个客户能够创建成功,那么就可以认为该客户端获取了锁。
同时,所有没有获取到锁的客户端就需要到/exclusive_lock
节点上注册一个子节点变更的watcher监听,以便实时监听到lock节点的变更情况
释放锁
/exclusive_lock/lock
是一个临时节点,因此在以下两种情况下,都有可能释放锁
-
当前获取锁的客户端机器发生宕机,那么ZooKeeper上的这个临时节点就会被移除
-
正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除
无论在什么情况下移除了lock节点,ZooKeeper都会通知所有在/exclusive_lock
节点上注册了子节点变更watcher监听的客户端。这些客户端在接收到通知后,再次重新发起分布式锁获取,即重复获取锁过程。推荐“Java精选面试题”小程序,内涵 3000+ 道面试题。