分布式锁一般有三种实现方式:

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的分布式锁实现的更多相关文章

  1. zookeeper实现分布式锁服务

    A distributed lock base on zookeeper. zookeeper是hadoop下面的一个子项目, 用来协调跟hadoop相关的一些分布式的框架, 如hadoop, hiv ...

  2. [ZooKeeper.net] 3 ZooKeeper的分布式锁

    基于ZooKeeper的分布式锁 ZooKeeper 里实现分布式锁的基本逻辑: 1.zookeeper中创建一个根节点(Locks),用于后续各个客户端的锁操作. 2.想要获取锁的client都在L ...

  3. 基于 Zookeeper 的分布式锁实现

    1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用.且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linu ...

  4. zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

  5. zookeeper 实现分布式锁安全用法

    zookeeper 实现分布式锁安全用法 标签: zookeeper sessionExpire connectionLoss 分布式锁 背景 ConnectionLoss 链接丢失 SessionE ...

  6. 基于Zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

  7. 转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁

    [ZooKeeper.net] 3 ZooKeeper的分布式锁   基于ZooKeeper的分布式锁  源码分享:http://pan.baidu.com/s/1miQCDKk ZooKeeper ...

  8. Redis与Zookeeper实现分布式锁的区别

    Redis实现分布式锁 1.根据lockKey区进行setnx(set not exist,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没 ...

  9. Zookeeper系列四:Zookeeper实现分布式锁、Zookeeper实现配置中心

    一.Zookeeper实现分布式锁 分布式锁主要用于在分布式环境中保证数据的一致性. 包括跨进程.跨机器.跨网络导致共享资源不一致的问题. 1. 分布式锁的实现思路 说明: 这种实现会有一个缺点,即当 ...

  10. 10分钟看懂!基于Zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

随机推荐

  1. Error configuring application listener of class [org.springframework.web.util.Log4jConfigListener]

    1.启动项目发现如下错误: 严重: Error configuring application listener of class [org.springframework.web.util.Log4 ...

  2. MFC 使程序不在任务栏显示

    在OnInitDialog()中直接修改窗口风格: // 让本程序不在任务栏显示(创建一个工具条窗口) ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW);

  3. React报错:Laravel React-Router browserHistory 刷新子页面报错404

    举例:myblog.com/ 刷新没问题 myblog.com/add 刷新404 browserHistory报404,hashHistory却正常 原因是少路由.web.php添加路由 Route ...

  4. light oj 1095 组合数学

    #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> ...

  5. 苹果CMS

    本篇将主要讲解使用过程中普遍遇到的“问题”,这些问题并非是BUG,通常是需要我们自己去注意的一些点.(会结合用户反馈持续补充)http://www.maccms.com/doc/v10/faq.htm ...

  6. PAT甲级——A1089 Insert or Merge

    According to Wikipedia: Insertion sort iterates, consuming one input element each repetition, and gr ...

  7. hibernate离线条件查询设置or关系

    detachedCriteria.add(Restrictions.or(Restrictions.isNull(""), Restrictions.isNull("&q ...

  8. VRRP概述-转

    本文介绍了VRRP的基本原理.特点和应用. VRRP概述 随着Internet的发展,人们对网络的可靠性的要求越来越高.对于局域网用户来说,能够时刻与外部网络保持联系是非常重要的. 通常情况下,内部网 ...

  9. HttpException (0x80004005): 无法连接到 SQL Server 会话数据库

    ASP.NET 项目运行时出现错误提示:[HttpException (0x80004005): 无法连接到 SQL Server 会话数据库.] ,后排查问题发现是由于项目的Session模式是使用 ...

  10. python print输出format太好用了

    不用担心什么其他的东西了,直接用format: print("{}的Ground,Detected,DetectedRight个数分别为{},{},{},".format(cate ...