zookeeper(4)--zookeeper分布式锁原理

时间:2022-05-29 05:38:23

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中,Java中其实提供了很多并发处理相关的API,但是这些API在分布式场景中就无能为力了。也就是说单纯的Java Api并不能提供分布式锁的能力。所以针对分布式锁的实现目前有多种方案。

针对分布式锁的实现,目前比较常用的有以下几种方案:

  基于数据库实现分布式锁 基于缓存(redis,memcached,tair)实现分布式锁 基于Zookeeper实现分布式锁

 一、zookeeper中分布式锁实现原理

(1)、普通节点思路

现在模拟一个使用Zookeeper实现分布式锁,假设有A,B,C三台客户端去访问资源,调用zookeeper获得锁。客户端三个在zookeeper的 /locks节点下创建一个/lock节点,由于节点是唯一性的特性,只有一个人会创建成功,其余两个创建失败,会进入监听/locks节点的变化,如果/locks下子节点/lock节点发生变化,其余两个可以去拿锁,这样是否好呢? 这样子会导致惊群效应。就是一个触发使得在短时间呢会触发大量的watcher事件,但是只有一个客户端能拿到锁

  --众人抢,大量watcher事件

zookeeper(4)--zookeeper分布式锁原理

(2)、有序节点思路

1、在获取分布式锁的时候在locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。

2、客户端调用createNode方法在locks下创建临时顺序节点,然后调用getChildren(“locks”)来获取locks下面的所有子节点,注意此时不用设置任何Watcher。

3、客户端获取到所有的子节点path之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。

4、如果发现自己创建的节点并非locks所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时对其注册事件监听器。

5、之后,让这个被关注的节点删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是locks子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

  zookeeper(4)--zookeeper分布式锁原理

二、代码实现

package com.lf.zookeeper.lock;

import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/*
*实现分布式锁
*/
public class DestributeLock implements Lock,Watcher{ private ZooKeeper zk = null;
private String ROOT_LOCK ="/locks";//定义根节点
private String CURRENT_LOCK;//当前锁
private String WAIT_LOCK ;//等待前一个对象释放锁 private CountDownLatch countDownLatch; public DestributeLock() {
try {
zk= new ZooKeeper("192.168.25.129:2181,192.168.25.130:2181,192.168.25.131:2181", 4000, this);
//判断根节点是否存在
Stat stat = zk.exists(ROOT_LOCK, false);
if(stat==null){
zk.create(ROOT_LOCK, "1".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE , CreateMode.PERSISTENT);
}
} catch (Exception e) {
e.printStackTrace();
}
} @Override
public void process(WatchedEvent event) { if(countDownLatch != null){
this.countDownLatch.countDown();
}
} @Override
public void lock() {
if(tryLock()){
System.out.println(Thread.currentThread().getName()+"->"+CURRENT_LOCK+",获取锁成功!");
return;
}
try {
waitForLock(WAIT_LOCK);//如果没有获得锁,继续等待
} catch (Exception e) {
e.printStackTrace();
}
} private boolean waitForLock(String prev) throws Exception, InterruptedException {
Stat stat = zk.exists(prev, true);
if(stat!=null){
System.out.println(Thread.currentThread().getName()+"->等待"+ROOT_LOCK+prev+"释放锁");
countDownLatch = new CountDownLatch(1);
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"->"+"获得锁成功!");
}
return true;
} @Override
public void lockInterruptibly() throws InterruptedException {
// TODO Auto-generated method stub } @Override
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
} @Override
public boolean tryLock() {
// TODO Auto-generated method stub
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();
SortedSet<String> lessthanMe = sortedSet.headSet(CURRENT_LOCK);
if(CURRENT_LOCK.equals(firstNode)){//当前节点和最小锁比较,如果相同,则获取锁成功
return true;
}
if(!lessthanMe.isEmpty()){
WAIT_LOCK = lessthanMe.last();//获取比当前节点更小的最后一个节点,设置给WAIT_LOCK
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return false;
} @Override
public boolean tryLock(long arg0, TimeUnit arg1) throws InterruptedException {
// TODO Auto-generated method stub
return false;
} @Override
public void unlock() {
System.out.println(Thread.currentThread().getName()+"->"+CURRENT_LOCK+"释放锁");
try {
zk.delete(CURRENT_LOCK, -1);
CURRENT_LOCK = null;
zk.close();
} catch (Exception e) {
// TODO: handle exception
}
} }

测试类

package com.lf.zookeeper.lock;

import java.io.IOException;
import java.util.concurrent.CountDownLatch; public class LockDemo { public static void main(String[] args) throws IOException {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 1; i <= 10; i++) {
new Thread(()->{
try {
countDownLatch.await();
DestributeLock destributeLock = new DestributeLock();
destributeLock.lock();
} catch (Exception e) {
e.printStackTrace();
}
},"Thread-"+i).start();
countDownLatch.countDown();
}
System.in.read();
}
}

运行结果

  

Thread-4->/locks/0000000072,尝试竞争锁!
Thread-5->/locks/0000000073,尝试竞争锁!
Thread-9->/locks/0000000074,尝试竞争锁!
Thread-8->/locks/0000000075,尝试竞争锁!
Thread-10->/locks/0000000076,尝试竞争锁!
Thread-2->/locks/0000000077,尝试竞争锁!
Thread-6->/locks/0000000078,尝试竞争锁!
Thread-3->/locks/0000000079,尝试竞争锁!
Thread-7->/locks/0000000080,尝试竞争锁!
Thread-1->/locks/0000000071,获取锁成功!
Thread-4->等待/locks/locks/0000000071释放锁
Thread-5->等待/locks/locks/0000000072释放锁
Thread-9->等待/locks/locks/0000000073释放锁
Thread-8->等待/locks/locks/0000000074释放锁
Thread-10->等待/locks/locks/0000000075释放锁
Thread-2->等待/locks/locks/0000000076释放锁
Thread-6->等待/locks/locks/0000000077释放锁
Thread-3->等待/locks/locks/0000000078释放锁
Thread-7->等待/locks/locks/0000000079释放锁

手动触发watcher事件,释放锁,delete /locks/0000000071

出现     Thread-4->/locks/0000000072,获取锁成功!

三、基于curator的实现分布式锁

  代码

  

package com.lf.zookeeper.lock;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex; public class CuratorLockDemo { public static void main(String[] args) {
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().build();
InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/locks");//关注节点
try {
interProcessMutex.acquire();//获取锁
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

zookeeper(4)--zookeeper分布式锁原理的更多相关文章

  1. 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

    首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...

  2. 女朋友也能看懂的Zookeeper分布式锁原理

      前言 关于分布式锁,在互联网行业的使用场景还是比较多的,比如电商的库存扣减,秒杀活动,集群定时任务执行等需要进程互斥的场景.而实现分布式锁的手段也很多,大家比较常见的就是redis跟zookeep ...

  3. zookeeper 分布式锁原理

    zookeeper 分布式锁原理: 1 大家也许都很熟悉了多个线程或者多个进程间的共享锁的实现方式了,但是在分布式场景中我们会面临多个Server之间的锁的问题,实现的复杂度比较高.利用基于googl ...

  4. Zookeeper--0300--java操作Zookeeper,临时节点实现分布式锁原理

    删除Zookeeper的java客户端有  : 1,Zookeeper官方提供的原生API, 2,zkClient,在原生api上进行扩展的开源java客户端 3, 一.Zookeeper原生API ...

  5. 基于Zookeeper实现多进程分布式锁

    一.zookeeper简介及基本操作 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化.当对目录节点监控状态打开时,一旦目录节点的状态发生变化,Watc ...

  6. 如何用Zookeeper来实现分布式锁&quest;

    什么是Zookeeper临时顺序节点? 例如 : / 动物 植物 猫 仓鼠 荷花 松树 Zookeeper的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做Zonde.# Znode分为四种类型 ...

  7. 基于zookeeper实现的分布式锁

    基于zookeeper实现的分布式锁 2011-01-27 • 技术 • 7 条评论 • jiacheo •14,941 阅读 A distributed lock base on zookeeper ...

  8. java使用zookeeper实现的分布式锁示例

    java使用zookeeper实现的分布式锁示例 作者: 字体:[增加 减小] 类型:转载 时间:2014-05-07我要评论 这篇文章主要介绍了java使用zookeeper实现的分布式锁示例,需要 ...

  9. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  10. Redis分布式锁原理

    1. Redis分布式锁原理 1.1. Redisson 现在最流行的redis分布式锁就是Redisson了,来看看它的底层原理就了解redis是如何使用分布式锁的了 1.2. 原理分析 分布式锁要 ...

随机推荐

  1. &lbrack;转载&rsqb; Linux 文件系统结构介绍

    原文: https://linux.cn/article-6132-weibo.html Linux中的文件是什么?它的文件系统又是什么?那些配置文件又在哪里?我下载好的程序保存在哪里了?在 Linu ...

  2. virtualbox安装增强包及配置共享文件夹

       因为需要在host及虚拟机间传输数据,想使用共享文件夹.但是单独设置了共享文件夹后在centos里找不到共享文件夹,看了下要安装增强包.好吧,顺 便也解决下鼠标切换的问题,省的老是按右CTL切换 ...

  3. Mac iOS-----PhoneGap 添加条形扫描码插件

    此随笔记录的是通过plugman来安装phonegap插件. 第1步:安装plugman npm install -g plugman QiaodeMacBook-Pro:MyApp root# np ...

  4. ASP&period;NET4&period;0新特性

    原文:ASP.NET4.0新特性 在以前试用VS2010的时候已经关注到它在Web开发支持上的一些变化了,为此我还专门做了一个ppt,当初是计划在4月12日那天讲的,结果因为莫名其妙的原因导致没有语音 ...

  5. Intellij IDEA设置运行时Java参数

    1.选中类,点击顶部菜单:Run-->Edit Configuration,如下所示: 2.选择Application,指定的类,在右边选择Configuration,并在VM options中 ...

  6. Python学习&lpar;三&rpar;:迭代器、生成器、装饰器、递归、算法、正则

    1.迭代器 迭代器是访问集合的一种方式,迭代对象从集合的第一个元素开始访问,直到元素被访问结束,迭代器只能往前不能后退,最大的优点是不要求事先准备好整个迭代过程中的元素,这个特点使得它特别适合用于遍历 ...

  7. pycharm安装破解go插件

    pycharm 2018以前版本安装go插件可以直接在plugins中搜索 go 插件,然后安装.若是2018以后的版本,go 变成了收费插件,plugins 搜不到.然后Google了一下,有人提供 ...

  8. Kinfu配置指南

    Kinfu配置指南 欢迎加入Kinfu讨论群:563741937 写给准备配置pcl-kinfu的同学,这个是我的配置方法,已经能用的请无视. 我的配置:Windows7,VS2010(用这个的是不是 ...

  9. clean

    启动tomcat 报 Could not delete D:/online/.metadata/.plugins/org.eclipse.wst.server.core/tm  

  10. python线程池(threadpool)

    一.安装 pip install threadpool 二.使用介绍 (1)引入threadpool模块 (2)定义线程函数 (3)创建线程 池threadpool.ThreadPool() (4)创 ...