分布式锁

  实现原理:有序节点+watch监听机制实现

分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同工具Zookeeper,当然也有着标准的实现方式。下面介绍在zookeeper中如果实现排他锁

设计思路

  1. 每个客户端往/Locks下创建临时有序节点/Locks/Lock_,创建成功后/Locks下面会有每个客户端对应的节点,如/Locks/Lock_000000001

  2. 客户端取得/Locks下子节点,并进行排序,判断排在前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功

  3. 如果自己的锁节点不在第一位,则监听自己前一位的锁节点。例如,自己锁节点Lock_000000002,那么则监听Lock_000000001

  4. 当前一位锁节点(Lock_000000001)对应的客户端执行完成,释放了锁,将会触发监听客户端(Lock_000000002)的逻辑

  5. 监听客户端重新执行第2步逻辑,判断自己是否获得了锁

  6. zookeeper是有工具包的(这里采用手写)
// 线程测试类
public class ThreadTest {
public static void delayOperation(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static interface Runable{
void run();
}
public static void run(Runable runable,int threadNum){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(30, 30,
0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
for (int i = 0; i < threadNum; i++) {
threadPoolExecutor.execute(runable::run);
}
threadPoolExecutor.shutdown();
} public static void main(String[] args) {
// DistributedLock distributedLock = new DistributedLock();
// distributedLock.acquireLock();
// delayOperation();
// distributedLock.releaseLock();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 每秒打印信息
run(() -> {
for (int i = 0; i < 999999999; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
String format = dateTimeFormatter.format(LocalDateTime.now());
System.out.println(format);
}
},1);
// 线程测试
run(() -> {
DistributedLock distributedLock = new DistributedLock();
distributedLock.acquireLock();
delayOperation();
distributedLock.releaseLock();
},30);
}
}
public class DistributedLock {
private String IP = "192.168.133.133:2181";
private final String ROOT_LOCK = "/Root_Lock";
private final String LOCK_PREFIX = "/Lock_";
private final CountDownLatch countDownLatch = new CountDownLatch(1);
private final byte[] DATA = new byte[0]; private ZooKeeper zookeeper;
private String path; private void init(){
// 初始化
try {
zookeeper = new ZooKeeper(IP, 200000, w -> {
if(w.getState() == Watcher.Event.KeeperState.SyncConnected){
System.out.println("连接成功");
}
countDownLatch.countDown();
});
countDownLatch.await();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
} // 暴露的外部方法,主逻辑
public void acquireLock(){
init();
createLock();
attemptLock();
} // 暴露的外部方法,主逻辑
public void releaseLock(){
try {
zookeeper.delete(path,-1);
System.out.println("锁释放了" + path);
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
} private void createLock(){
try {
// 创建一个目录节点
Stat root = zookeeper.exists(ROOT_LOCK, false);
if(root == null)
zookeeper.create(ROOT_LOCK, DATA, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 目录下创建子节点
path = zookeeper.create(ROOT_LOCK + LOCK_PREFIX, DATA, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
private Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted){
synchronized (this){
this.notifyAll();
}
}
}
}; private void attemptLock(){
try {
// 获取正在排队的节点,由于是zookeeper生成的临时节点,不会出错,这里不能加监视器
// 因为添加了监视器后,任何子节点的变化都会触发监视器
List<String> nodes = zookeeper.getChildren(ROOT_LOCK,false);
nodes.sort(String::compareTo);
// 获取自身节点的排名
int ranking = nodes.indexOf(path.substring(ROOT_LOCK.length() + 1));
// 已经是最靠前的节点了,获取锁
if(ranking == 0){
return;
}else {
// 并不是靠前的锁,监视自身节点的前一个节点
Stat status = zookeeper.exists(ROOT_LOCK+"/"+nodes.get(ranking - 1), watcher);
// 有可能这这个判断的瞬间,0号完成了操作(此时我们应该判断成功自旋才对),但是上面的status变量已经获取了值并且不为空,1号沉睡
// 但是,请注意自行测试,虽然1号表面上沉睡了,但是实际上watcher.wait()是瞬间唤醒的
if(status == null){
attemptLock();
}else {
synchronized (watcher){
watcher.wait();
}
attemptLock();
}
}
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
}

7、zookeeper应用场景-分布式锁的更多相关文章

  1. 整理分布式锁:业务场景&分布式锁家族&实现原理

    1.引入业务场景 业务场景一出现: 因为小T刚接手项目,正在吭哧吭哧对熟悉着代码.部署架构.在看代码过程中发现,下单这块代码可能会出现问题,这可是分布式部署的,如果多个用户同时购买同一个商品,就可能导 ...

  2. 如何用Zookeeper来实现分布式锁?

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

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

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

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

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

  5. ZooKeeper 笔记(6) 分布式锁

    目前分布式锁,比较成熟.主流的方案有基于redis及基于zookeeper的二种方案. 大体来讲,基于redis的分布式锁核心指令为SETNX,即如果目标key存在,写入缓存失败返回0,反之如果目标k ...

  6. 基于zookeeper实现高性能分布式锁

    实现原理:利用zookeeper的持久性节点和Watcher机制 具体步骤: 1.创建持久性节点 zkLock 2.在此父节点下创建子节点列表,name按顺序定义 3.Java程序获取该节点下的所有顺 ...

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

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

  8. 利用ZooKeeper简单实现分布式锁

    1.分布式锁的由来: 在程序开发过程中不得不考虑的就是并发问题.在java中对于同一个jvm而言,jdk已经提供了lock和同步等.但是在分布式情况下,往往存在多个进程对一些资源产生竞争关系,而这些进 ...

  9. 基于zookeeper简单实现分布式锁

    https://blog.csdn.net/desilting/article/details/41280869 这里利用zookeeper的EPHEMERAL_SEQUENTIAL类型节点及watc ...

  10. zookeeper实现的分布式锁

    在分布式系统中,多个jvm对共享资源进行操作时候,要加上锁,这就是分布式锁 利用zookeeper的临时节点的特性,可以实现分布式锁 public class ZookeeperDistrbuteLo ...

随机推荐

  1. [2] HEVD 学习笔记:栈溢出漏洞训练

    2. HEVD 栈溢出漏洞训练 2.1 漏洞原理 ​ 当函数退出的时候,会将保存在栈中的返回地址取出,跳转到该地址继续执行,以此来执行函数调用以后的程序.而如果用户的输入没有得到控制,覆盖掉了这个返回 ...

  2. C/C++ 发送与接收HTTP/S请求

    HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议.它是一种无状态的.应用层的协议,用于在计算机之间传输超文本文档,通常在 Web 浏览器和 Web 服务器之 ...

  3. SpringBoot 整合多数据源的事务问题

    代码 先贴代码: 核心就是:Spring给我们提供的一个类 AbstractRoutingDataSource,然后我们再写一个切面来切换数据源,肯定要有一个地方存储key还要保证上下文都可用,所以我 ...

  4. 关于 vant 移动端的 rem 适配方案

    一.使用 lib-flexible 动态设置 rem 基准值 (html 标签的字体大小) (1) 安装依赖: npm i amfe-flexible -D (2) 在main.js 中引入 impo ...

  5. SpringBoot中优雅地实现统一响应对象

    目录 前言 实现步骤 定义统一响应对象类 定义一个忽略响应封装的注解 实现ResponseBodyAdvice接口 定义Controller类 总结 前言 近日心血来潮想做一个开源项目,目标是做一款可 ...

  6. electron 安装不同的版本的方法

    1.官网:http://www.electronjs.org/ 2.思考,既然是npm 安装,那么肯定也在 npm中央仓库有,那么去中央仓库看下: npm i -D electron@11.0.4

  7. .NET Core开发实战(第4课:Startup:掌握ASP.NET Core的启动过程)--学习笔记

    04 | Startup:掌握ASP.NET Core的启动过程 新建一个 ASP.NET Core Web 应用程序 选择 API public class Program { public sta ...

  8. PostgreSQL-可以通过localhost连接,无法通过IP地址连接。

    (1)如果PostgreSQL配置文件中没有允许访问该服务器的IP地址,则需要先添加允许访问的IP地址,并在防火墙中开放相应的端口.(2)在PostgreSQL配置文件postgresql.conf中 ...

  9. MySQL-面试知识点汇总

    1. DQL相关 2. DDL.DML.DCL相关 3.架构相关 3.1 MySQL的复制原理以及流程 主从复制:将主数据库中的DDL和DML操作通过二进制日志(BINLOG)传输到从数据库上,然后将 ...

  10. NC18987 粉嘤花之恋

    题目链接 题目 题目描述 qn是个特别可爱的小哥哥,qy是个特别好的小姐姐,他们两个是一对好朋友 [ cp (划掉~) 又是一年嘤花烂漫时,小qn于是就邀请了qy去嘤花盛开的地方去玩.当qy和qn来到 ...