基于zookeeper实现分布式锁

欢迎查看Eetal的第二十七篇博客–基于zookeeper实现分布式锁

分布式锁设计

zookeeper的临时节点在会话断开后,则会自动删除,这个特性使其可以非常好的应用在分布式锁
而如果直接使用某一个path的临时节点作为分布式锁,会导致”惊群效应“(当锁每次释放都会唤醒全部竞争节点,而实际只能有一个竞争到)
所以结合有序节点进行优化,对于某一个特定的节点RootNode创建临时有序子节点
当一个客户端要获取锁时,就会新增一个临时有序子节点
在尝试获取锁时进行判断,查看RootNode当前的所有子节点信息,判断自己是否是最小的子节点(因为zookeeper的有序节点会从1递增)
如果是最小的子节点,则获得锁,在释放锁时,删除自己创建的这个临时有序节点
如果不是最小节点,代表还有别人在占用锁,则注册一个事件监听子节点中节点序号刚好小于自己的节点的删除事件,当监听到事件时,则代表获取锁成功
因为是临时节点,保证了出现某一台客户端占据锁但是断开连接仍然会删除其创建的临时节点
通过这个设计避免了”惊群效应“,某一个锁的释放只会触发刚好晚于他创建临时有序节点的的下一个等待锁的客户端的事件,由其获得锁

demo源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 基于zk实现分布式锁
*/
public class DistributedLock implements Watcher {
String ROOT_LOCK;//作为锁的命名空间标识
String WAIT_LOCK;//正在等待的锁的路径
String CURRENT_LOCK;//当前请求对应的锁
ZooKeeper zk;
CountDownLatch countDownLatch;

public DistributedLock(ZooKeeper zk,String ROOT_LOCK)throws Exception{
this.zk = zk;
this.ROOT_LOCK = ROOT_LOCK;
if(zk.exists(ROOT_LOCK,false) == null){
throw new Exception("RootLock node not exists!");
}
}

public boolean tryLock() {
try {
CURRENT_LOCK = zk.create(ROOT_LOCK+"/","0".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
List<String> children = zk.getChildren(ROOT_LOCK,false);
SortedSet<String> sortChildren = new TreeSet<String>();
sortChildren.addAll(children);
//获得排序小于CURRENT_LOCK的childNode的path
SortedSet<String> lessThanMe = sortChildren.headSet(CURRENT_LOCK);
if(lessThanMe.isEmpty())
return true;
else{
//存在小于自己的节点,获取失败
WAIT_LOCK = lessThanMe.last();
return false;
}
} catch (Exception e){
e.printStackTrace();
}
return false;
}

public void lock() {
while(!tryLock()){
//出现异常则继续tryLock
try {
if(zk.exists(WAIT_LOCK,this)!=null){
countDownLatch = new CountDownLatch(1);
countDownLatch.await();
//获得锁,结束
return;
}
} catch (Exception e){
e.printStackTrace();
}
}
}

public void unlock() {
try {
zk.delete(CURRENT_LOCK,-1);
} catch (Exception e){
e.printStackTrace();
}
}

public void process(WatchedEvent event) {
if(event.getType() == Event.EventType.NodeDeleted)
countDownLatch.countDown();
}

}

第三方工具也有提供基于zookeeper实现的更多分布式锁的设计,如curator的recipes工程

更多精彩内容

请移步

个人主页: yangyitao.top