ZooKeeper的分布式锁实现
分布式锁一般有三种实现方式:
1. 数据库乐观锁;
2. 基于Redis的分布式锁;
3. 基于ZooKeeper的分布式锁。
本篇博客将介绍第三种方式,基于Zookeeper实现分布式锁。虽然网上已经有各种介绍Zookeeper分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Zookeeper分布式锁。
现在模拟一个使用Zookeeper实现分布式锁,假设有A,B,C三台客户端去访问资源,调用zookeeper获得锁。客户端三个在zookeeper的 /locks节点下创建一个/lock节点,由于节点是唯一性的特性,只有一个人会创建成功,其余两个创建失败,会进入监听/locks节点的变化,如果/locks下子节点/lock节点发生变化,其余两个可以去拿锁,这样是否好呢? 这样子会导致惊群效应。就是一个触发使得在短时间呢会触发大量的watcher事件,但是只有一个客户端能拿到锁。所以这种方式不建议。
有一种比较好的方法就是利用 zookeeper 的有序节点的特性,基本思路:
1、在获取分布式锁的时候在locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。
2、客户端调用createNode方法在locks下创建临时顺序节点,然后调用getChildren(“locks”)来获取locks下面的所有子节点,注意此时不用设置任何Watcher。
3、客户端获取到所有的子节点path之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。
4、如果发现自己创建的节点并非locks所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时对其注册事件监听器。
5、之后,让这个被关注的节点删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是locks子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。
下面看一下我的代码实现:
public class DistributedLock implements Lock,Watcher {
private ZooKeeper zk=null;
private String ROOT_LOCK="/locks"; //定义根节点
private String WAIT_LOCK; //等待前一个锁
private String CURRENT_LOCK; //表示当前的锁
// 作为阻塞
private CountDownLatch countDownLatch; //
public DistributedLock() {
try {
zk=new ZooKeeper("192.168.254.135:2181",
4000,this);
//判断根节点是否存在
Stat stat=zk.exists(ROOT_LOCK,false);
if(stat==null){//如果不存在创建
zk.create(ROOT_LOCK,"0".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
/**
* 尝试获取锁
*/
@Override
public boolean tryLock() {
try {
//创建临时有序节点
CURRENT_LOCK=zk.create(ROOT_LOCK+"/","0".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(Thread.currentThread().getName()+"->"+
CURRENT_LOCK+",尝试竞争锁");
List<String> childrens=zk.getChildren(ROOT_LOCK,false); //获取根节点下的所有子节点
SortedSet<String> sortedSet=new TreeSet();//定义一个集合进行排序
for(String children:childrens){ // 排序
sortedSet.add(ROOT_LOCK+"/"+children);
}
String firstNode=sortedSet.first(); //获得当前所有子节点中最小的节点
// 取出比我创建的节点还小的节点,没有的话为null
SortedSet<String> lessThenMe=((TreeSet<String>) sortedSet).headSet(CURRENT_LOCK);
if(CURRENT_LOCK.equals(firstNode)){//通过当前的节点和子节点中最小的节点进行比较,如果相等,表示获得锁成功
return true;
}
if(!lessThenMe.isEmpty()){
WAIT_LOCK=lessThenMe.last();//获得比当前节点更小的最后一个节点,设置给WAIT_LOCK
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
@Override
public void lock() {
if(this.tryLock()){ //如果获得锁成功
System.out.println(Thread.currentThread().getName()+"->"+CURRENT_LOCK+"->获得锁成功");
return;
}
try {
waitForLock(WAIT_LOCK); //没有获得锁,继续等待获得锁
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private boolean waitForLock(String prev) throws KeeperException, InterruptedException {
//监听当前节点的上一个节点 注册事件,这里需要在默认的 watch 事件里面处理
// 这里是我们之前提到的 watch 事件触发最后执行的 process 回调里面的 请看最下行代码
Stat stat=zk.exists(prev,true);
if(stat!=null){
System.out.println(Thread.currentThread().getName()+"->等待锁"+ROOT_LOCK+"/"+prev+"释放");
countDownLatch=new CountDownLatch(1);
countDownLatch.await();// 进入等待,这里需要
//TODO watcher触发以后,还需要再次判断当前等待的节点是不是最小的
System.out.println(Thread.currentThread().getName()+"->获得锁成功");
}
return true;
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
System.out.println(Thread.currentThread().getName()+"->释放锁"+CURRENT_LOCK);
try {
// -1 表示无论如何先把这个节点删了再说
zk.delete(CURRENT_LOCK,-1);
CURRENT_LOCK=null;
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
@Override
public Condition newCondition() {
return null;
}
@Override
public void process(WatchedEvent event) {
// 事件回调 countDownLatch.countDown();
if(this.countDownLatch!=null){
this.countDownLatch.countDown();
}
}
}
代码中实现了 Lock,Watcher 两个接口。主要用到的是lock 里面的trylock方法,尝试去获取锁。然后还有watcher里面的处理回调的方法
测试代码
public static void main( String[] args ) throws IOException {
CountDownLatch countDownLatch=new CountDownLatch(10);
for(int i=0;i<10;i++){
new Thread(()->{
try {
countDownLatch.await();
DistributedLock distributedLock=new DistributedLock();
distributedLock.lock(); //获得锁
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Thread-"+i).start();
countDownLatch.countDown();
}
System.in.read();
}
运行结果:
Thread-8->/locks/0000000040,尝试竞争锁
Thread-5->/locks/0000000044,尝试竞争锁
Thread-9->/locks/0000000041,尝试竞争锁
Thread-3->/locks/0000000042,尝试竞争锁
Thread-7->/locks/0000000046,尝试竞争锁
Thread-1->/locks/0000000043,尝试竞争锁
Thread-0->/locks/0000000047,尝试竞争锁
Thread-4->/locks/0000000045,尝试竞争锁
Thread-2->/locks/0000000049,尝试竞争锁
Thread-6->/locks/0000000048,尝试竞争锁
Thread-8->/locks/0000000040->获得锁成功
Thread-9->等待锁/locks//locks/0000000040释放
Thread-5->等待锁/locks//locks/0000000043释放
Thread-0->等待锁/locks//locks/0000000046释放
Thread-1->等待锁/locks//locks/0000000042释放
Thread-2->等待锁/locks//locks/0000000048释放
Thread-7->等待锁/locks//locks/0000000045释放
Thread-6->等待锁/locks//locks/0000000047释放
Thread-3->等待锁/locks//locks/0000000041释放
Thread-4->等待锁/locks//locks/0000000044释放
ZooKeeper的分布式锁实现的更多相关文章
- zookeeper实现分布式锁服务
A distributed lock base on zookeeper. zookeeper是hadoop下面的一个子项目, 用来协调跟hadoop相关的一些分布式的框架, 如hadoop, hiv ...
- [ZooKeeper.net] 3 ZooKeeper的分布式锁
基于ZooKeeper的分布式锁 ZooKeeper 里实现分布式锁的基本逻辑: 1.zookeeper中创建一个根节点(Locks),用于后续各个客户端的锁操作. 2.想要获取锁的client都在L ...
- 基于 Zookeeper 的分布式锁实现
1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用.且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linu ...
- zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...
- zookeeper 实现分布式锁安全用法
zookeeper 实现分布式锁安全用法 标签: zookeeper sessionExpire connectionLoss 分布式锁 背景 ConnectionLoss 链接丢失 SessionE ...
- 基于Zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...
- 转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁
[ZooKeeper.net] 3 ZooKeeper的分布式锁 基于ZooKeeper的分布式锁 源码分享:http://pan.baidu.com/s/1miQCDKk ZooKeeper ...
- Redis与Zookeeper实现分布式锁的区别
Redis实现分布式锁 1.根据lockKey区进行setnx(set not exist,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没 ...
- Zookeeper系列四:Zookeeper实现分布式锁、Zookeeper实现配置中心
一.Zookeeper实现分布式锁 分布式锁主要用于在分布式环境中保证数据的一致性. 包括跨进程.跨机器.跨网络导致共享资源不一致的问题. 1. 分布式锁的实现思路 说明: 这种实现会有一个缺点,即当 ...
- 10分钟看懂!基于Zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...
随机推荐
- String方法之fromCharCode()和charCodeAt()
1.fromCharCode fromCharCode() 可接受一个指定的 Unicode 值,然后返回一个字符串. 语法 我们可以根据 Unicode 来输出 "HELLO" ...
- MYSQL批量创建表的存储过程
因为业务需要,创建了100个表,但是这些表的结构都是一样的,作为程序员,就是要解决这种重复劳动.然而这种事情还要单独写个php脚本的话太麻烦了吧,所以就干脆学了一下直接用Mysql存储过程怎么实现: ...
- ROC曲线及AUC
ROC曲线 意义 ROC曲线指受试者工作特征曲线 / 接收器操作特性曲线(receiver operating characteristic curve),是反映敏感性和特异性连续变量的综合指标,是用 ...
- IDEA本地SBT项目上传到SVN
需求 将本地创建的一个项目上到SVN 网上很多从SVN下载到idea,提交.更新.删除等操作. 但是少有从本地上传一个项目到svn管理的案例 本文参考https://blog.csdn.net/cao ...
- Charles抓包(http/https请求)
Charles安装 HTTP抓包 HTTPS抓包 1. Charles安装官网下载安装Charles:https://www.charlesproxy.com/download/当然由于国情可以使用破 ...
- 在菜单栏对应图标点击右键-关闭窗口,javaw.exe进程未关闭。
问题: 可视化开发时,运行一个工程,总会生成一个javaw.exe进程. 关闭运行程序,javaw.exe还存在. 解决: 运行java工程时,会启动一个新的虚拟机来运行你的程序. 程序退出的时候,这 ...
- Django的日常-视图层
目录 Django的日常-3 JsonResponse form表单上传文件 CBV的源码分析 视图层 模板传值 过滤器 标签 自定义过滤器和标签 Django的日常-3 JsonResponse 在 ...
- 05-1-操作css样式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- ORA-01790: 表达式必须具有与对应表达式相同的数据类型
出现这种错误,要先看一下是不是sql中有用到连接:union,unio all之类的,如果有,需要注意相同名称字段的数据类型一定要相同.
- 2014-VGG-《Very deep convolutional networks for large-scale image recognition》翻译
2014-VGG-<Very deep convolutional networks for large-scale image recognition>翻译 原文:http://xues ...