SpringBoot基于Zookeeper实现分布式锁

在这里插入图片描述

文章目录

  • 问题背景
  • 前言
  • 实现
    • 搭建Zookeeper容器
    • 引入依赖
    • ZK客户端的配置类
    • ZK客户端的工厂类
    • 注入bean
    • 构建测试类

问题背景

研究分布式锁,基于ZK实现,需要整合到SpringBoot使用

前言

  1. 参考自SpringBoot集成Curator实现Zookeeper基本操作,Zookeeper入门
  2. 本篇的代码笔者有自己运行过,需要注意组件的版本号是否兼容,否则会有比较多的坑

实现

搭建Zookeeper容器

采用Docker compose快速搭建ZK容器,很快,几分钟就好了,而且是集群方式搭建。详情见笔者的Docker搭建zookeeper

引入依赖

需要注意的点:Curator 2.x.x-兼容两个zk 3.4.xzk 3.5.xCurator 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);}}}
}

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

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

相关文章

AUTOSAR规范与ECU软件开发(实践篇)3.3 AUTOSAR系统解决方案介绍(下)

示例将遵循AUTOSAR方法论来进行开发&#xff0c; 所用的AUTOSAR解决方案如图3.6所示。 图3.6 AUTOSAR系统解决方案 首先&#xff0c; 使用Matlab/Simulink来实现部分软件组件级的开发&#xff0c; 主要包括LightRequestSWC和LightControlSWC&#xff0c; 并自动生成应用层软件组…

基于STM32的ADC采样及各式滤波实现(HAL库,含VOFA+教程)

前言&#xff1a;本文为手把手教学ADC采样及各式滤波算法的教程&#xff0c;本教程的MCU采用STM32F103ZET6。以HAL库的ADC采样函数为基础进行教学&#xff0c;通过各式常见滤波的实验结果进行分析对比&#xff0c;搭配VOFA工具直观的展示滤波效果。ADC与滤波算法都是嵌入式较为…

图神经网络 day2 图的分类

图神经网络基础算法 1 GCN2 GraphSAGE2.1 采样&#xff1a;采样固定长度的邻居2.2 聚合2.3 GraphSAGE_minibatch2.4 GraphSAGE_embedding 3 GAT4. 图网络的分类4.1 递归图神经网络 RGNN4.2 图卷积神经网络GCN4.3 图注意力网络 GAT4.4 图自动编码 GAE4.5 图时空网络 GSTN4.6 图生…

栈存储结构详解

目录 栈存储结构详解 进栈和出栈 栈的具体实现 栈的应用 什么是队列&#xff08;队列存储结构&#xff09; 栈存储结构详解 同顺序表和链表一样&#xff0c;栈也是用来存储逻辑关系为 "一对一" 数据的线性存储结构&#xff0c;如图 1 所示。 图 1 栈存储结构示意…

【Redis】Redis三种集群模式-主从、哨兵、集群各自架构的优点和缺点对比

文章目录 前言1. 单机模式2. 主从架构3. 哨兵4. 集群模式总结 前言 如果Redis的读写请求量很大&#xff0c;那么单个实例很有可能承担不了这么大的请求量&#xff0c;如何提高Redis的性能呢&#xff1f;你也许已经想到了&#xff0c;可以部署多个副本节点&#xff0c;业务采用…

数据结构基础

将节点构建成树 数据的结构逻辑结构集合线性结构树形结构图状结构 存储结构合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如…

髋关节 弹响

评估测试 https://www.bilibili.com/video/BV1A44y1j71Y/?spm_id_from333.880.my_history.page.click&vd_source3535bfaa5db8443d107998d15e88dc44 根据此视频整理所得 托马斯测试 第一种情况 如果你难于将膝关节拉到胸前&#xff0c;并感觉前面有骨性的挤压 说明你股…

HTML详解连载(3)

HTML详解连载&#xff08;3&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽表单作用使用场景 input标签基本使用示例type属性值以及说明 input标签占位文本示例注意 单选框 radio代码示例 多选框-checkbox注意代码示例 文本域作用标签&#xff1…

主流的嵌入式微处理器

目前主流的嵌入式微处理器系列有&#xff1a; ARM系列 MIPS系列 PowerPC系列 Super H系列 一、MPC/PPC系列 PowerPC(简称PPC),其基本设计源自IBM的POWER.1991年&#xff0c;APPLE(苹果电脑)、IBM、Motorola&#xff08;摩托罗拉&#xff09;组成的AIM联盟发展出Power微处理器…

Unity 工具 之 Azure 微软SSML语音合成TTS流式获取音频数据的简单整理

Unity 工具 之 Azure 微软SSML语音合成TTS流式获取音频数据的简单整理 目录 Unity 工具 之 Azure 微软SSML语音合成TTS流式获取音频数据的简单整理 一、简单介绍 二、实现原理 三、实现步骤 四、关键代码 一、简单介绍 Unity 工具类&#xff0c;自己整理的一些游戏开发可…

ViewFs And Federation On HDFS

序言 ViewFs 是在Federation的基础上提出的,用于通过一个HDFS路径来访问多个NameSpace,同时与ViewFs搭配的技术是client-side mount table(这个就是具体的规则配置信息可以放置在core.xml中,也可以放置在mountTable.xml中). 总的来说ViewFs的其实就是一个中间层,用于去连接不…

css伪元素实现li列表圆点相连+锚点跳转悬浮窗实现

实现效果&#xff1a; html代码&#xff1a; <div class"sidenav"><ul class"nav-text progressbar"><!-- data-target的值对应要跳转的模块的id --><li data-target"module1"><div class"text">锚点…