02-zookeeper分布式锁案例

1 Zookeeper分布式案例

1.1 Zookeeper分布式锁原理

核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。

当我们假设根节点/ 下有/locks节点时

1)客户端获取锁时,在locks节点下创建临时顺序节点。

2)然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。(即需要小的优先)使用完锁后,将删除该结点。

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

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

1.2 分布式锁案例分析

  • 客户端获取到锁时创建临时顺序节点 create -e -s /locks/seq-
  • 接收到请求后,在/locks节点下创建一个临时顺序节点
  • 判断自己是不是当前节点下最小的节点,如果是,获取到锁;如果不是,对前一个节点进行监听
  • 获取到锁,处理完业务以后,delete节点释放锁,然后下面的节点将会收到通知,重复上述判断

1.2.1 原生Zookeeper实现代码实现

package com.clear.case2;import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;/*** 分布式锁案例*/
public class DistributedLock {private final String connectString = "kk01:2181,kk02:2181,kk01:2181";private final int sessionTimeout = 2000;private final ZooKeeper zk;private CountDownLatch countDownLatch = new CountDownLatch(1);private CountDownLatch waitLatch = new CountDownLatch(1);private String waitPath;private String currentMode;public DistributedLock() throws IOException, InterruptedException, KeeperException {// 获取连接zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent event) {//  countDownLatch 如果连接上zk,可以释放if (event.getState() == Event.KeeperState.SyncConnected) {countDownLatch.countDown();}// waitLatch 可以释放if (event.getType() == Event.EventType.NodeDeleted && event.getPath().equals(waitPath)) {waitLatch.countDown();}System.out.println("");}});// 等待zk正常连接后再执行后续程序countDownLatch.await();// 判断根节点/locks是否存在Stat stat = zk.exists("/locks", false);if (stat == null) {// 创建根节点(根节点为持久节点)zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}}// 对zk加锁public void zkLock() {// 创建对应的临时带序号的节点try {currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);// 判断创建的节点是否是根目录下最小节点,如果是获得锁;如果不是,监听它序号前一个节点List<String> children = zk.getChildren("/locks", false);// 如果children只有一个值,那就直接获取锁,如果有多个节点,那就需要判断谁最小if (children.size() == 1) {return;} else {Collections.sort(children);// 获取节点名称 seq-0000...String thisNode = currentMode.substring("/locks/".length());// 通过 seq-0000... 获取其在集合children 中的位置int index = children.indexOf(thisNode);// 判断if (index == -1) {System.out.println("数据异常");} else if (index == 0) {// 就一个节点,获取到了锁return;} else {// 监听它前一个节点waitPath = "/locks/" + children.get(index - 1);zk.getData(waitPath, true, null);// 等待监听waitLatch.await();return;}}} catch (KeeperException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}// 对zk解锁public void unZkLock() {// 删除节点try {zk.delete(currentMode, -1);} catch (InterruptedException e) {e.printStackTrace();} catch (KeeperException e) {e.printStackTrace();}}
}

1.2.2 测试代码

package com.clear.case2;import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;/*** 分布式锁案例*/
public class DistributedLockTest {public static void main(String[] args) throws InterruptedException, IOException, KeeperException {final DistributedLock lock1 = new DistributedLock();final DistributedLock lock2 = new DistributedLock();new Thread(new Runnable() {@Overridepublic void run() {try {lock1.zkLock();System.out.println("线程1 启动,获取到锁");Thread.sleep(5000);lock1.unZkLock();System.out.println("线程1 释放锁");} catch (InterruptedException e) {e.printStackTrace();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {try {lock2.zkLock();System.out.println("线程2 启动,获取到锁");Thread.sleep(5000);lock2.unZkLock();System.out.println("线程2 释放锁");} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

启动后,结果如下

线程1 启动,获取到锁
线程1 释放锁线程2 启动,获取到锁
线程2 释放锁

两个线程不会同时得到锁,此致,分布式锁案例完成

1.2.3 Curator 框架实现分布式案例

1)原生的 Java API 开发存在的问题

  • 会话连接是异步的,需要自己去处理,比如使用 CountDownLatch
  • Watch 需要重复注册,不然就不能生效
  • 开发的复杂性还是比较高的
  • 不支持多节点删除和创建。需要自己去递归

2)Curator是一个专门解决分布式锁的框架,解决了原生Java API 开发分布式遇到的问题

Cutator官方文档 https://curator.apache.org/index.html

1、导入依赖

<!-- curator--><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-client</artifactId><version>4.0.0</version></dependency>

2、代码实现

package com.clear.case3;import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.log4j.Logger;public class CuratorLockTest {private final static Logger logger = Logger.getLogger(CuratorLockTest.class);public static void main(String[] args) {// 创建分布式锁1InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");// 创建分布式锁2InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");new Thread(new Runnable() {@Overridepublic void run() {try {lock1.acquire();System.out.println("线程1 获取到锁");lock1.acquire();System.out.println("线程1 再次获取到锁");Thread.sleep(5000);lock1.release();System.out.println("线程1 释放锁");lock1.release();System.out.println("线程1 再次释放锁");} catch (Exception exception) {exception.printStackTrace();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {try {lock2.acquire();System.out.println("线程2 获取到锁");lock2.acquire();System.out.println("线程2 再次获取到锁");Thread.sleep(5000);lock2.release();System.out.println("线程2 释放锁");lock2.release();System.out.println("线程2 再次释放锁");} catch (Exception exception) {exception.printStackTrace();}}}).start();}private static CuratorFramework getCuratorFramework() {CuratorFramework client = CuratorFrameworkFactory.builder().connectString("kk01:2181,kk02:2181,kk03:2181").connectionTimeoutMs(2000).sessionTimeoutMs(2000).retryPolicy(new ExponentialBackoffRetry(3000, 3)).build();// 启动客户端client.start();logger.info("zookeeper启动成功");return client;}
}

结果如下

线程2 获取到锁
线程2 再次获取到锁
线程2 释放锁
线程2 再次释放锁
线程1 获取到锁
线程1 再次获取到锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/71833.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go小知识2

Golang开发新手常犯的50个错误_gezhonglei2007的博客-CSDN博客 一些题目整理&#xff0c;附带大佬的解释 1.go中哪些值不能寻址& 常量&#xff08;const常量&#xff0c;字面值3.14&#xff0c;字符串“xxx”&#xff0c;函数或方法, map的val值&#xff09; golang中接…

Python、Rust中的协程

协程 协程在不同的堆栈上同时运行&#xff0c;但每次只有一个协程运行&#xff0c;而其调用者则等待: F启动G&#xff0c;但G并不会立即运行&#xff0c;F必须显式的恢复G&#xff0c;然后 G 开始运行。在任何时候&#xff0c;G 都可能转身并让步返回到 F。这会暂停 G 并继续…

OpenCV(二十五):边缘检测(一)

目录 1.边缘检测原理 2.Sobel算子边缘检测 3.Scharr算子边缘检测 4.两种算子的生成getDerivKernels() 1.边缘检测原理 其原理是基于图像中灰度值的变化来捕捉图像中的边界和轮廓。梯度则表示了图像中像素强度变化的强弱和方向。 所以沿梯度方向找到有最大梯度值的像素&…

Linux网络编程 网络基础知识

目录 1.网络的历史和协议的分成 2.网络互联促成了TCP/IP协议的产生 3.网络的体系结构 4.TCP/IP协议族体系 5.网络各层的协议解释 6.网络的封包和拆包 7.网络预备知识 1.网络的历史和协议的分成 Internet-"冷战"的产物 1957年十月和十一月&#xff0c;前苏…

C#__线程的优先级和状态控制

线程的优先级&#xff1a; 一个CPU同一时刻只能做一件事情&#xff0c;哪个线程优先级高哪个先运行&#xff0c;优先级相同看调度算法。 在Thread类中的Priority属性&#xff08;Highest,Above,Normal,BelowNormal,Lowest&#xff09;可以影响线程的优先级 关于…

swiper删除虚拟slide问题

在存在缓存的情况下&#xff0c;删除较前的slide&#xff0c;会出现当前slide与后一个slide重复出现的情况 假设当前存在5个slide&#xff0c;且这5个slide已缓存&#xff0c;则删除slide2后&#xff0c;仍为5个slide&#xff0c;且slide2的内容变为slide3的内容&#xff0c;此…

Pushgetway安装和使用

1、Pushgetway安装和使用 1.1 Pushgateway是什么 pushgateway 是另一种数据采集的方式&#xff0c;采用被动推送来获取监控数据的prometheus插件&#xff0c;它可以单独运行在 任何节点上&#xff0c;并不一定要运行在被监控的客户端。 首先通过用户自定义编写的脚本把需要监…

记一次时间序列算法的自回归预测--ARAutoreg

背景 最近公司给客户要做一些数据的预测&#xff0c;但是客户不清楚哪些做起来比较符合他们的&#xff0c;于是在经过与业务方的沟通&#xff0c;瞄准了两个方面的数据 1.工程数据&#xff1a;对工程数据做评估&#xff0c;然后做预警&#xff0c;这个想法是好的&#xff0c;…

物流供应商实现供应链自动化的3种方法

当前影响供应链的全球性问题(如新冠肺炎疫情)正在推动许多物流供应商重新评估和简化其流程。运输协调中的摩擦只会加剧供应商无法控制的现有延误和风险。值得庆幸的是&#xff0c;供应链专业人员可以通过端到端的供应链自动化消除延迟&#xff0c;简化与合作伙伴的沟通&#xf…

DGIOT-Modbus-RTU控制指令05、06的配置与下发

[小 迪 导 读]&#xff1a;伴随工业物联网在实际应用中普及&#xff0c;Modbus-RTU作为行业内的标准化通讯协议。在为物联网起到采集作用的同时&#xff0c;设备的控制也是一个密不可分的环节。 场景解析&#xff1a;在使用Modbus对设备进行采集后&#xff0c;可以通过自动控制…

Python 正则表达式:强大的文本处理工具

概念&#xff1a; 正则表达式是一种强大的文本匹配和处理工具&#xff0c;它可以用来在字符串中查找、替换和提取符合某种规则的内容。在Python中&#xff0c;使用re模块可以轻松地操作正则表达式&#xff0c;它提供了丰富的功能和灵活的语法。 场景&#xff1a; 正则表达式…

多波束测线问题

多波束测线问题 问题的背景是海洋测深技术&#xff0c;特别是涉及单波束测深和多波束测深系统。这些系统利用声波传播原理来测量水体深度。 单波束测深系统通过向海底发射声波信号并记录其返回时间来测量水深。该系统的特点是每次只有一个波束打到海底&#xff0c;因此数据分布…

理解项目开发(寺庙小程序)

转载自&#xff1a;历经一年&#xff0c;开发一个寺庙小程序&#xff01; (qq.com) 破防了&#xff01;为方丈开发一款纪念小程序&#xff01; (qq.com) 下面内容转载自&#xff1a;程序员5K为青岛啤酒节开发个点餐系统&#xff01; (qq.com) 看一个人如何完成一个项目的开发…

CSS笔记(黑马程序员pink老师前端)浮动,清除浮动

浮动可以改变标签的默认排列方式。浮动元素常与标准流的父元素搭配使用. 网页布局第一准则:多个块级元素纵向排列找标准流&#xff0c;多个块级元素横向排列找浮动。 float属性用于创建浮动框&#xff0c;将其移动到一边&#xff0c;直到左边缘或右边缘触及包含块或另一个浮动框…

分类预测 | MATLAB实现PCA-LSTM(主成分长短期记忆神经网络)分类预测

分类预测 | MATLAB实现PCA-LSTM(主成分长短期记忆神经网络)分类预测 目录 分类预测 | MATLAB实现PCA-LSTM(主成分长短期记忆神经网络)分类预测预测效果基本介绍程序设计参考资料致谢 预测效果 基本介绍 MATLAB实现PCA-LSTM(主成分长短期记忆神经网络)分类预测。Matlab实现基于P…

Python基础List列表定义与函数

如何定义一个非空的列表&#xff1f; name_list ["liming","xiaohong",15,{"hobby":"basketball"}] 列表的特点&#xff1a; 1.列表是有序的 2.可以存放多个元素 3.每个元素可以是任何数据类型 定义一个空列表 name_list [] 访…

【java】【项目实战】[外卖十一]项目优化(Ngnix)

目录 一、Nginx概述 1、Nginx介绍 2、Nginx下载和安装 3、Nginx目录结构 二、Nginx命令 1、查看版本 2、检查配置文件正确性 3、启动和停止 4、重新加载配置文件 三、Nginx配置文件结构 1、全局块 2、events块 3、http块 四、Nginx具体应用 1、部署静态资源 2、…

LeetCode 904. 水果成篮

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 在你去摘水果的时候&#xff0c;你当前只能拥有两种种类的水果&#xff0c;若想拿第三种水果&#xff0c;就需要发下前两种水果中的一种。 法一&#xff1a;滑动窗口哈希表(未优化…

【Linux】shell脚本和bat脚本:

文章目录 一、脚本对应环境&#xff1a;【1】shell&#xff1a;linux环境&#xff1b;后缀名为.sh【2】bat&#xff1a;windows环境&#xff1b;后缀名为.bat或者.cmd 二、脚本执行&#xff1a;【1】shell执行【2】bat脚本执行 三、脚本相关命令&#xff1a;1. shell命令【1】s…

C++中的多态和虚函数

#include <iostream>using namespace std;//基类Peopleclass People{public:People(char *name, int age);void display();protected:char *m_name;int m_age;};People::People(char *name, int age): m_name(name), m_age(age){}void People::display(){cout<<m_n…