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

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. synchronized ReentrantLock 比较分析

    在编写多线程代码的时候,对于不允许并发的代码,很多需要加锁进行处理.在进行加锁处理时候,synchronized作为java的内置锁,同时也是java关键字,最为被人熟知,即使是最初级的java程序员 ...

  2. HTTP各版本的区别

    什么是HTTP和HTTPS? HTTP是浏览器与服务器之间以明文的方式传送内容的一种互联网通信协议. HTTPS是在HTTP的基础上主要基于SPDF协议结合SSL/TLS加密协议,客户端依靠证书验证服 ...

  3. Vue生命周期的执行过程(面试必备) 极简版

    最近准备面试,临时抱佛脚的来回顾一下vue相关的面试题,当然这是不对的,平时还是要努力呀,走起: 1.创建vue实例,Vue(); 2.在创建Vue实例的时候,执行了init(),在init过程中首先 ...

  4. 【牛客挑战赛31D】 雷的打字机

    题目 首先看到这个出现长度至少为\(2\)的回文子串 这就等价于不能出现两个连续且相同的字符 于是我们用概率生成函数来搞 设\(g_i\)表示\(i\)次操作后游戏没有结束的概率,\(f_{i,j}\ ...

  5. Mybatis Plus 入坑(含最新3.X配置)

    简介 Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发.提高效率而生. 使用它可以简化单表的操作, 节省开发时间, 国人 ...

  6. QC的安装和配置

    QC(Quality center)的安装配置 Wmware 虚拟机 数据库SQL server2000 Windows server 2003 须安装数据库的sp4补丁包 注意事项 数据库安装时选择 ...

  7. Jmeter接口测试(第二篇)

    一.新建项目 1.运行Jmeter.bat打开Jmeter 2.添加线程组(测试计划->添加->Thread(users)->线程组) 3.添加HTTP请求(线程组->添加-& ...

  8. 记录:vue结合springboot进行分页查询和按条件进行查询

    界面: 主要代码: 搜索框: <el-form ref="searchForm" :inline="true" :model="searchMa ...

  9. php报错:Notice: iconv(): Wrong charset, conversion from `GBK' to `UTF8' is not allowed

    代码写错了 $raw_data = iconv("GBK", "UTF8", $raw_data); 改成 $raw_data = iconv("GB ...

  10. 微信小程序注册使用流程

    新开发微信小程序使用流程:  平时使用小程序较多,但是具体注册流程简单记录下: 第一步:通过邮箱注册 第二步:在邮箱进行激活 注册后,在邮箱会收到激活信息提示.点击激活地址进行激活. 第三步:信息登记 ...