SpringBoot基于Zookeeper实现分布式锁

文章目录
- 问题背景
- 前言
- 实现
- 搭建Zookeeper容器
- 引入依赖
- ZK客户端的配置类
- ZK客户端的工厂类
- 注入bean
- 构建测试类
问题背景
研究分布式锁,基于ZK实现,需要整合到SpringBoot使用
前言
- 参考自SpringBoot集成Curator实现Zookeeper基本操作,Zookeeper入门
- 本篇的代码笔者有自己运行过,需要注意组件的版本号是否兼容,否则会有比较多的坑
实现
搭建Zookeeper容器
采用Docker compose快速搭建ZK容器,很快,几分钟就好了,而且是集群方式搭建。详情见笔者的Docker搭建zookeeper
引入依赖
需要注意的点:
Curator 2.x.x-兼容两个zk 3.4.x和zk 3.5.x,Curator 3.x.x-兼容兼容zk 3.5,根据搭建的zk的版本使用对应的curator依赖。引入的zk依赖,如果项目中有使用logback日志 ,需要排除zk中的log4j12依赖,详情见下面笔者给出的依赖:
<dependencies><dependency><groupId>org.apache.curator</groupId><artifactId>curator-client</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.5.7</version><exclusions><exclusion><artifactId>slf4j-log4j12</artifactId><groupId>org.slf4j</groupId></exclusion><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>
ZK客户端的配置类
配置ZK的参数,使用
@ConfigurationProperties可以令配置热更新,比如搭配Apollo、Nacos,如果使用@Valid则无法热更新,必须重启项目才能生效
@Component
@ConfigurationProperties(prefix = "curator")
@Data
public class ZKClientProps {private String connectString;private int retryCount;private int elapsedTimeMs;private int sessionTimeoutMs;private int connectionTimeoutMs;
}
对应yml如下:
#curator配置
curator:connectString: 192.168.163.128:2181,192.168.163.128:2182,192.168.163.128:2183 # zookeeper 地址retryCount: 1 # 重试次数elapsedTimeMs: 2000 # 重试间隔时间sessionTimeoutMs: 60000 # session超时时间connectionTimeoutMs: 10000 # 连接超时时间
ZK客户端的工厂类
定制ZK客户端:
@Component
public class ZKClientFactory {@Resourceprivate ZKClientProps zkClientProps;public CuratorFramework createSimple() {//重试策略:第一次重试等待1S,第二次重试等待2S,第三次重试等待4s//第一个参数:等待时间的基础单位,单位为毫秒//第二个参数:最大重试次数ExponentialBackoffRetry retry = new ExponentialBackoffRetry(zkClientProps.getElapsedTimeMs(), zkClientProps.getRetryCount());//获取CuratorFramework示例的最简单方式//第一个参数:zk的连接地址//第二个参数:重试策略return CuratorFrameworkFactory.newClient(zkClientProps.getConnectString(), retry);}public static CuratorFramework createWithOptions(String connectionString, RetryPolicy retryPolicy,int connectionTimeoutMs, int sessionTimeoutMs) {return CuratorFrameworkFactory.builder().connectString(connectionString).retryPolicy(retryPolicy).connectionTimeoutMs(connectionTimeoutMs).sessionTimeoutMs(sessionTimeoutMs).build();}
}
注入bean
创建ZK的客户端,详情如下:
@Component
@Slf4j
public class ZKClient {@Resourceprivate ZKClientFactory zkClientFactory;public static final ZKClient INSTANCE = new ZKClient();private ZKClient() {}public CuratorFramework getClient() {return zkClientFactory.createSimple();}public boolean isNodeExist(String path) {CuratorFramework client = getClient();try {client.start();Stat stat = client.checkExists().forPath(path);return stat != null;} catch (Exception e) {e.printStackTrace();} finally {CloseableUtils.closeQuietly(client);}return false;}public void createNode(String path, byte[] bytes) {CuratorFramework client = getClient();try {// 必须start,否则报错client.start();client.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, bytes);} catch (Exception e) {e.printStackTrace();} finally {CloseableUtils.closeQuietly(client);}}public void deleteNode(String path) {CuratorFramework client = getClient();try {client.start();client.delete().forPath(path);} catch (Exception e) {e.printStackTrace();} finally {CloseableUtils.closeQuietly(client);}}public List<String> getChildren(String path) {List<String> result = new LinkedList<>();CuratorFramework client = getClient();try {client.start();result = client.getChildren().forPath(path);} catch (Exception e) {log.error("ZKClient getChildren error.");}return result;}}
构建测试类
测试基类,设置激活环境
@Slf4j
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = GmallZookeeperApplication.class)
@ContextConfiguration
public class BaseTest {}
创建节点、删除节点、获取节点信息、分布式锁的方法如下:
@ActiveProfiles("company")是激活笔者一个application-company.yml文件
application.yml如下:
server:port: 8022spring:profiles:active: home
application-compay.yml如下:
#curator配置
curator:connectString: 192.168.163.128:2181,192.168.163.128:2182,192.168.163.128:2183 # zookeeper 地址retryCount: 1 # 重试次数elapsedTimeMs: 2000 # 重试间隔时间sessionTimeoutMs: 60000 # session超时时间connectionTimeoutMs: 10000 # 连接超时时间
创建节点、删除节点、获取节点信息、分布式锁的方法如下:
@Slf4j
@ActiveProfiles("company")
public class ZKClientTest extends BaseTest{@Resourceprivate ZKClient zkClient;public static final int THREAD_NUM = 10;@Testpublic void distributedLock() throws InterruptedException, BrokenBarrierException {String lockPath = "/test/distributed2/lock";CuratorFramework client = zkClient.getClient();client.start();InterProcessMutex lock = new InterProcessMutex(client, lockPath);// 阻塞主线程,等待全部子线程执行完CyclicBarrier cyclicBarrier = new CyclicBarrier(THREAD_NUM);for (int i = 0; i < THREAD_NUM; i++) {new Thread(() -> {log.info("{}->尝试竞争锁", Thread.currentThread().getName());try {lock.acquire(); // 阻塞竞争锁log.info("{}->成功获得锁", Thread.currentThread().getName());Thread.sleep(2000);cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();} finally {try {lock.release(); //释放锁} catch (Exception e) {e.printStackTrace();}}}, "Thread-" + i).start();}// 目的是为了等子线程抢完锁再结束子线程,否则无法看到日志效果cyclicBarrier.await();log.info("全部子线程已执行完毕");}@Testpublic void createNode() {// 创建一个ZNode节点String data = "hello";byte[] payload = data.getBytes(StandardCharsets.UTF_8);String zkPath = "/test/CRUD/node-1";zkClient.createNode(zkPath, payload);log.info("createNode succeeded!");}@Testpublic void getChildren() {String zkPath = "/test/CRUD";List<String> children = zkClient.getChildren(zkPath);printList(children);}@Testpublic void deleteNode() {String parentPath = "/test";log.info("======================Before delete===================");List<String> before = zkClient.getChildren(parentPath);printList(before);String zkPath = "/test/CRUD/node-1";zkClient.deleteNode(zkPath);log.info("delete node secceeded!");log.info("======================After delete===================");List<String> after = zkClient.getChildren(parentPath);printList(after);}private void printList(List<String> data) {if (!CollectionUtils.isEmpty(data)) {for (String datum : data) {log.info("datum:{}", data);}}}
}
相关文章:
SpringBoot基于Zookeeper实现分布式锁
文章目录 问题背景前言实现搭建Zookeeper容器引入依赖ZK客户端的配置类ZK客户端的工厂类注入bean构建测试类 问题背景 研究分布式锁,基于ZK实现,需要整合到SpringBoot使用 前言 参考自SpringBoot集成Curator实现Zookeeper基本操作,Zookeeper入…...
AT89C51单片机实现单片机串口互动(中断方式,单片机--单片机,应答)
说一下功能:客户机发送0x01到服务机 2服务单片机应答0xf2到客户机 3客户机接收到0xf2,发送信息153432这6个数字到服务机 4client发送完信息后发送0xaa结束通信 5server接收到0xaa后回复0xaa结束通信,从此老死不相往来 看代码: //发送端…...
九耶丨阁瑞钛伦特-请说说你在工作中的PRD文档是如何撰写的?
1、背景说明(解释清楚为什么要做这样一件事,以及做这件事的价值,先把观点拉齐,才方便接下来的工作开展) 简要介绍与项目相关的背景信息、项目要满足的用户需求、开展项目的主要原因、项目期望上线时间、项目涉及的具体…...
Android免打包多渠道统计如何实现
摘要: 实际上只要完成1-2步即可实现多渠道打包,这也意味着,只要每次更新App时给出一个原始包,运营人员就能在后台自己进行操作管理,简单快捷到全程无需开发人员参与。 我们都知道,Android 市场被分割成几十…...
Apipost CICD怎么配置?
配置CI/CD Apipost自动化测试新增CI/CD,配置运行环境、循环次数、间隔停顿后点击保存会生成命令,在安装Apipost的服务器中输入命令即可运行测试脚本。 自动化测试 创建自动化测试脚本在创建好的测试用例中选择「CICD」,点击新建,…...
utf-8和utf-8 mb4区别
UTF-8(Unicode Transformation Format-8)和UTF-8MB4(UTF-8 Multibyte 4-byte)是字符编码方案,用于表示 Unicode 字符集中的字符。它们之间的主要区别在于编码范围。 UTF-8:UTF-8 是一种变长编码方式&#x…...
考研 408 | 【计算机网络】 应用层
导图 网络应用模型 客户/服务器(c/s)模型 P2P模型 DNS 域名 域名服务器 域名解析过程 文件传输协议FTP FTP服务器和用户端 FTP工作原理 电子邮件 电子邮件的信息格式 组成结构 邮件服务器的功能: 1.发送&接收邮件 2.给发件人报告邮…...
设计模式-单例
概述 在类加载后,整个系统只有一个实例类 饿汉式 public class Mg1 {private static final Mg1 INSTANCE new Mg1();private Mg1(){}public static Mg1 getInstance(){return INSTANCE;}public static void main(String[] args) {System.out.println(Mg1.getIns…...
mysql截取最后一个字符之前的数据
1、mysql截取最后一个字符之前的数据 select --截取斜杠之前的数据REVERSE(SUBSTR(REVERSE(SPNH-dfg-2012) ; --截取斜杠后的数据 INSTR(REVERSE(SPNH-fg-2012),-)1))2、mysql获取最后一个字符后的数据 select SUBSTRING_INDEX(SPNH-dfg-2012,-,-1) 3、mysql更新某个字段…...
Flutter 中,ListView 中需要放置 ListView 需要怎么处理才高效?
问题及场景 ListView 是 Flutter 开发者第一个学习到的 Widget,因为它可以滑动。一切都会运行得很好,直到 ListView 中的 Item 本身也是一个 ListView。你可能会看到 Flutter 建议你将内部的 ListView 的ShrinkWrap 属性设置为 True。虽然错误消除了&am…...
Appium Desktop安装
【提示:官方已不再维护,建议命令行方式安装,但可以学习了解一下】 Appium Desktop是一款适用于Mac、Windows和Linux的应用程序,它以漂亮灵活的UI为您提供Appium自动化服务器的强大功能。它基本上是Appium Server的图形界面。您可…...
Open3D 最小二乘拟合平面(SVD分解法)
目录 一、算法原理二、代码实现三、结果展示1、点云2、拟合结果四、优秀博客本文由CSDN点云侠原创,原文链接。爬虫网站自重。 一、算法原理 本文实现矩阵奇异值分解方法的最小二乘拟合平面。原理如下: 对于得到的 n n...
Pytorch源码搜索与分析
PyTorch的的代码主要由C10、ATen、torch三大部分组成的。其中: C10 C10,来自于Caffe Tensor Library的缩写。这里存放的都是最基础的Tensor库的代码,可以运行在服务端和移动端。PyTorch目前正在将代码从ATen/core目录下迁移到C10中。C10的代…...
运维监控学习笔记9
2、画出拓扑图的小案例: 3、在连接的线上显示网络流量,使用了一个简单的公式: {nginx-server:net.if.out[ens33].last(0)} 4、在screens中显示nginx的状态页面: 5、zabbix报警: 发送邮件的选项。Email可以使用…...
gulimall-缓存-缓存使用
文章目录 前言一、本地缓存与分布式缓存1.1 使用缓存1.2 本地缓存1.3 本地模式在分布式下的问题1.4 分布式缓存 二、整合redis测试2.1 引入依赖2.2 配置信息2.3 测试 三、改造三级分类业务3.1 代码改造 四、高并发下缓存失效问题4.1 缓存穿透4.2 缓存雪崩4.3 缓存击穿 五、分布…...
概述、搭建Redis服务器、部署LNP+Redis、创建Redis集群、连接集群、集群工作原理
Top NSD DBA DAY09 案例1:搭建redis服务器案例2:常用命令限案例3:部署LNPRedis案例4:创建redis集群 1 案例1:搭建redis服务器 1.1 具体要求如下 在主机redis64运行redis服务修改服务运行参数 ip 地址192.168.88.6…...
redis数据类型与底层数据结构对应关系
对应关系如下 SDSZipListHashTableQuickListintsetSkipListString✔Hash✔✔List✔Set✔✔Zset✔✔ String SDS Hash ZipList 对应对象编码 OBJ_ENCODING_ZIPLIST HashTable 对应对象编码 OBJ_ENCODING_HT 当一个Hash对象的键值对数据量增加到一定数量时就会触发编码转换…...
SpringBoot请求响应
简单参数 1. 原始方式获取请求参数 Controller方法形参中声明httpServletRequest对象 调用对象的getParameter参数名 RestController public class RequestController {RequestMapping("/simpleParam")public String simpleParam(HttpServletRequest request){Strin…...
功能上新|全新GPU性能优化方案
GPU优化迎来了全新的里程碑!我们深知移动游戏对高品质画面的追求日益升温,因此UWA一直着眼于移动设备GPU性能优化,以确保您的游戏体验尽善尽美。然而,不同GPU芯片之间的性能差异及可能导致的GPU瓶颈问题,让优化工作变得…...
试岗第一天问题
1、公司的一个项目拉下来 ,npm i 不管用显示 后面百度 使用了一个方法 虽然解决 但是在增加别的依赖不行,后面发现是node版本过高,更换node版本解决。 2、使用插件动态的使数字从0到100(vue-animate-number插件) 第一…...
SABIC塑料与宏裕塑胶的卓越合作:高性能材料的行业应用
导读:在制造业转型升级的关键时期,高性能工程塑料的应用正成为企业提升产品竞争力的核心要素。SABIC塑料与宏裕塑胶的卓越合作,为行业提供了从原料选型到技术落地的完整解决方案,这种强强联合的模式正在重新定义高端材料供应体系。…...
如何用OpCore-Simplify在10分钟内完成黑苹果自动化配置:终极指南
如何用OpCore-Simplify在10分钟内完成黑苹果自动化配置:终极指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为复杂的黑苹果配置而…...
宣传片微电影制作拍摄
荣誉见证实力・匠心铸就品牌|国隆映像传媒,6 年深耕乌鲁木齐,斩获全国影像盛典、脱贫攻坚、文旅代言等多项大奖,为企事业单位提供一站式影视制作服务。...
数字孪生 · 零基础4周速成学习计划(书籍+实操+项目落地)
适合:零基础、物联网专业、想转行数字孪生、做项目、毕设、求职学习搭配:理论书籍 软件实操 协议打通 完整Demo项目第一周:建立体系(看懂数字孪生到底是什么)📚 阅读书籍:《数字孪生及车间实…...
如何将短信从Android传输到 iPhone
每次苹果发布新款 iPhone,都会吸引大量渴望更换手机的用户,其中也包括许多Android用户。对于这些Android用户来说, 将数据从Android迁移到新 iPhone是当务之急,尤其是传输短信,因为短信通常包含个人和职业生活的重要信…...
告别高斯模糊!用OpenCV+Python手把手实现引导滤波,保留图像边缘细节(附完整代码)
边缘保持滤波新选择:OpenCV与Python实现引导滤波实战指南 在数字图像处理领域,平滑滤波与边缘保持一直是一对难以调和的矛盾。传统的高斯滤波虽然能有效去除噪声,却常常以牺牲图像细节为代价;双边滤波虽然在一定程度上解决了边缘保…...
GHelper:华硕笔记本性能调优的轻量级革命
GHelper:华硕笔记本性能调优的轻量级革命 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Expertbook, RO…...
抖音直播弹幕实时采集:基于Golang的高性能解决方案
抖音直播弹幕实时采集:基于Golang的高性能解决方案 【免费下载链接】douyin-live-go 抖音(web) 弹幕爬虫 golang 实现 项目地址: https://gitcode.com/gh_mirrors/do/douyin-live-go 在直播电商和内容创作蓬勃发展的今天,实时获取抖音直播间的弹幕…...
华硕笔记本终极性能优化方案:G-Helper轻量级控制工具完全指南
华硕笔记本终极性能优化方案:G-Helper轻量级控制工具完全指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenb…...
如何实现快速排名?冷门制造业网站的3天起步法
小型机械厂、精细化工厂、模具厂拥有小众的工业产品。工业产品在网络上的搜索量极低。一款直径 50 毫米的硬质合金钻头,全球每月搜索量仅有 120 次。高空作业平台零部件的搜索量低至 50 次。极低的搜索热度带来一个现象:大型网络平台不参与这类词汇的竞争…...
