关于如何用Zookeeper实现分布式锁机制
之前对zk的应用场景比较好奇,后来发现hive当中已经有使用zk来实现分布式锁的实现。为了更好的理解分布式锁的实现,所以对hive这块的代码进行阅读,提升了对技术实现的理解。hive使用的是ZooKeeperHiveLockManager类来进行处理的。hive锁目前有两个类型,代码如下:public enum HiveLockMode{ SHARED, EXCL
之前对zk的应用场景比较好奇,后来发现hive当中已经有使用zk来实现分布式锁的实现。
为了更好的理解分布式锁的实现,所以对hive这块的代码进行阅读,提升了对技术实现的理解。
hive使用的是ZooKeeperHiveLockManager类来进行处理的。
hive锁目前有两个类型,代码如下:
public enum HiveLockMode{
SHARED, EXCLUSIVE;
}
ZooKeeperHiveLockManager此类获取锁主要是这个方法如下:
private ZooKeeperHiveLock lockPrimitive(HiveLockObject key,
HiveLockMode mode, boolean keepAlive, boolean parentCreated)
throws KeeperException, InterruptedException
调用路径如下:
通过此图可以发现来做查询的时候会获取读写锁,另一个是在对表做相关操作时会有锁表的获取。
第一步:首先是创建父节点列表。节点的形式:/parent1/parent2/parent3/lastname/
代码如下:
for (String name : names) {
try {
res = createChild(name, new byte[0], CreateMode.PERSISTENT);
} catch (KeeperException e) {
if (e.code() != KeeperException.Code.NODEEXISTS) {
//if the exception is not 'NODEEXISTS', re-throw it
throw e;
}
}
}
其中names是一个list类型,将String类转换成集合来进行,存储的数据是["/parent1","/parent1/parent2","/parent1/parent2/parent3",
"/parent1/parent2/parent3/lastname"];
第二步:创建lock节点,
res = createChild(getLockName(lastName, mode), key.getData().toString()
.getBytes(), keepAlive ? CreateMode.PERSISTENT_SEQUENTIAL : CreateMode.EPHEMERAL_SEQUENTIAL);
getlockName返回的其实是一个子节点名称,还是以之前的节点为例,那么这里创建的是/parent1/parent2/parent3/lastname/LOCK-{mode}-
其中mode为锁的模式(排他锁还是共享锁)。
CreateMode取值说明如下:
/**
* The znode will not be automatically deleted upon client's disconnect.
*/
PERSISTENT (0, false, false),
/**
* The znode will not be automatically deleted upon client's disconnect,
* and its name will be appended with a monotonically increasing number.
*/
PERSISTENT_SEQUENTIAL (2, false, true),
/**
* The znode will be deleted upon the client's disconnect.
*/
EPHEMERAL (1, true, false),
/**
* The znode will be deleted upon the client's disconnect, and its name
* will be appended with a monotonically increasing number.
*/
EPHEMERAL_SEQUENTIAL (3, true, true);
这里就是利用到了2和3类型的节点的特性,会在前缀的后面使用自增数字(这也是实现分布式锁的关键)。这样在下面的代码片段中,才能截取数字出来。
res的值当中是包含一个返回的序列号,把这个序列号截取出来,代码如下:
int seqNo = getSequenceNumber(res, getLockName(lastName, mode));
然后再次遍历lastName的子节点,代码如下:
List<String> children = zooKeeper.getChildren(lastName,false);
String exLock = getLockName(lastName, HiveLockMode.EXCLUSIVE);
String shLock = getLockName(lastName, HiveLockMode.SHARED);
for (String child : children) {
child = lastName + "/" + child;
// Is there a conflicting lock on the same object with a lower sequence
// number
int childSeq = seqNo;
if (child.startsWith(exLock)) {
childSeq = getSequenceNumber(child, exLock);
}
if ((mode == HiveLockMode.EXCLUSIVE) && child.startsWith(shLock)) {
childSeq = getSequenceNumber(child, shLock);
}
if ((childSeq >= 0) && (childSeq < seqNo)) {
zooKeeper.delete(res, -1);
console.printError("conflicting lock present for "
+ key.getDisplayName() + " mode " + mode);
return null;
}
}
这段代码就是处理锁的核心业务,首先判断是否是排他锁,如果是获取该锁的序列号;再次判断获取锁的模式为排他锁并且当前child的锁为共享锁,也获取
该锁的序列号。 然后判断语句childSeq<SeqNo,获取锁表明是否靠前,如果有childSeq<SeqNo为true,表示前面有人获取锁,那么这次获取锁是失败
的。换句话说,也就是根据这个自增数字来判断谁先获取到锁。
这也是如何实现分布式锁的核心。当然删除一个锁很简单,就是将对应的lock节点删除即可。
更多推荐
所有评论(0)