【Zookeeper】Zookeeper实现分布式锁
zookeeper实现分布式锁
一:zookeeper分布式锁原理
1:原子性
Zookeeper有写操作有原子性,利用这个特性可以实现分布式锁。
对于来自客户端的每个更新请求,ZooKeeper 具备严格的顺序访问控制能力。
为了保证事务的顺序一致性,ZooKeeper 采用了递增的事务 id 号(zxid)来标识事务。
2:Watcher
回调机制
客户端注册监听它关心的 znode,当 znode 状态发生变化(数据变化、子节点增减变化)时,ZooKeeper 服务会通知客户端。
客户端和服务端保持连接一般有两种形式:
- 客户端向服务端不断轮询
- 服务端向客户端推送状态
Zookeeper 的选择是服务端主动推送状态,也就是观察机制( Watch )。
二:实现分布式锁
我们可以通过Zookeeper的临时节点和watcher机制实现分布式锁
1:实现tryLock
public static ThreadLocal<CuratorZookeeperClient> zkClient = null;
public static String LOCK_NAME = "/lock";
public static ThreadLocal<String> CURRENT_NODE = new ThreadLocal<>();
static {
zkClient.set(new CuratorZookeeperClient("localhost:8080",2000,2000,null,new RetryOneTime(1)));
}
@Override
public boolean tryLock() {
String nodeName = LOCK_NAME + "/zk_";
try {
CURRENT_NODE.set(zkClient.get().getZooKeeper().create(nodeName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL));
List<String> children = zkClient.get().getZooKeeper().getChildren(nodeName, false);
Collections.sort(children);
// 判断是否获得锁
String minNode = children.get(0);
if ((LOCK_NAME +"/"+minNode).equals(CURRENT_NODE.get())){
// 获取到锁
return true;
}
// 没有获取到锁,监听前一个节点是否存在
// 获取前一个节点的index
int i = children.indexOf(CURRENT_NODE.get().substring(CURRENT_NODE.get().lastIndexOf("/") + 1));
// 获取前一个节点
String preNodeName = children.get(i);
// 增加countDownLatch 同步线程
CountDownLatch countDownLatch = new CountDownLatch(1);
zkClient.get().getZooKeeper().exists(LOCK_NAME + "/" + preNodeName, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
// 监听前一个节点,前一个临时节点消失就是代表当前线程为最小的那个节点,可以获取到锁
if (Event.EventType.NodeDeleted.equals(watchedEvent.getType())){
countDownLatch.countDown();
}
}
});
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
二:解锁:删除节点就可以了
@Override
public void unlock() {
try {
zkClient.get().getZooKeeper().delete(CURRENT_NODE.get(),-1);
CURRENT_NODE.remove();
} catch (Exception e) {
e.printStackTrace();
}finally{
zkClient.get().close();
}
}
这只是个demo没有经过测试的,主要展示一下逻辑
三:和Redis分布式锁的优劣势
优点:不存在redis的超时、数据同步(zookeeper是同步完以后才返回)、主从切换(zookeeper主从切换的过程中服务是不可用的)的问题,可靠性很高。
缺点:依赖中间件,保证了可靠性的同时牺牲了一部分效率(但是依然很高)。性能不如redis。