分布式锁redisson
1:pom.xml添加依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.21.1</version>
</dependency>
2-1:方法一:读取默认yml配置
# redis配置
spring:redis:database: 0host: 127.0.0.1password: 123456port: 6379
2-2:redisson.yml文件的方式
spring:redis:redisson:file: classpath:redisson.yml
2-3:redisson.yml
# 单节点配置
singleServerConfig:# 数据库编号database: 0# 节点地址address: redis://127.0.0.1:6379# 密码password: 123456
# 集群模式
clusterServersConfig:# 集群节点地址nodeAddresses:- "redis://127.0.0.1:16379"- "redis://127.0.0.1:26379"- "redis://127.0.0.1:36379"password: 123456
#Redis集群不支持多个数据库的概念,默认只有一个数据库,即db 0,所以这里是没有database这个参数的
3-1:方法二:自定义yml配置
spring:redis:# redisson配置redisson:# 如果该值为false,系统将不会创建RedissionClient的bean。enabled: true# mode的可用值为,single/cluster/sentinel/master-slavemode: single# single: 单机模式# address: redis://localhost:6379# cluster: 集群模式# 每个节点逗号分隔,同时每个节点前必须以redis://开头。# address: redis://localhost:6379,redis://localhost:6378,...# sentinel:# 每个节点逗号分隔,同时每个节点前必须以redis://开头。# address: redis://localhost:6379,redis://localhost:6378,...# master-slave:# 每个节点逗号分隔,第一个为主节点,其余为从节点。同时每个节点前必须以redis://开头。# address: redis://localhost:16379,redis://localhost:26379address: redis://127.0.0.1:6379# redis 密码,空可以不填。password: 123456database: 0
3-2:RedissonConfig自定义配置类
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Redisson配置类。*/
@Configuration
@ConditionalOnProperty(name = "spring.redis.redisson.enabled", havingValue = "true")
public class RedissonConfig {@Value("${spring.redis.redisson.mode}")private String mode;/*** 仅仅用于sentinel模式。*/@Value("${spring.redis.redisson.masterName:}")private String masterName;@Value("${spring.redis.redisson.address}")private String address;@Value("${spring.redis.redisson.password:}")private String password;/*** 数据库默认0*/@Value("${spring.redis.redisson.database:0}")private Integer database;@Beanpublic RedissonClient redissonClient() {if (StringUtils.isBlank(password)) {password = null;}Config config = new Config();if ("single".equals(mode)) {config.useSingleServer().setDatabase(database).setPassword(password).setAddress(address);} else if ("cluster".equals(mode)) {String[] clusterAddresses = address.split(",");config.useClusterServers()//集群模式不支持多个数据库概念,默认db 0.setPassword(password).addNodeAddress(clusterAddresses);} else if ("sentinel".equals(mode)) {String[] sentinelAddresses = address.split(",");config.useSentinelServers().setDatabase(database).setPassword(password).setMasterName(masterName).addSentinelAddress(sentinelAddresses);} else if ("master-slave".equals(mode)) {String[] masterSlaveAddresses = address.split(",");if (masterSlaveAddresses.length == 1) {throw new IllegalArgumentException("redis.redisson.address MUST have multiple redis addresses for master-slave mode.");}String[] slaveAddresses = new String[masterSlaveAddresses.length - 1];System.arraycopy(masterSlaveAddresses, 1, slaveAddresses, 0, slaveAddresses.length);config.useMasterSlaveServers().setDatabase(database).setPassword(password).setMasterAddress(masterSlaveAddresses[0]).addSlaveAddress(slaveAddresses);} else {throw new IllegalArgumentException(mode);}return Redisson.create(config);}
}
4:demo
@Resourceprivate RedissonClient redissonClient;private static final String LOCK_KEY = "myLock";@SneakyThrows@GetMapping("test")public void test() {// 获取锁对象RLock lock = redissonClient.getLock(LOCK_KEY);// 模拟多个线程尝试获取锁for (int i = 0; i < 5; i++) {new Thread(() -> {try {// 尝试获取锁,最多等待10秒,获取锁后10秒自动释放lock.lock(10, TimeUnit.SECONDS);System.out.println(new Date()+Thread.currentThread().getName() + " 获取到锁,开始处理任务...");Thread.sleep(2000); // 模拟处理任务需要花费一些时间System.out.println(new Date()+Thread.currentThread().getName() + " 处理任务完成,释放锁...");} catch (InterruptedException e) {e.printStackTrace();} finally {// 无论如何,最后都要确保锁被释放lock.unlock();}}).start();}// 让主线程等待一段时间,以便观察其他线程的行为Thread.sleep(10000);System.out.println("===");}
4-1:运行结果
5-1:自定义注解分布式锁JLock
package com.huan.study.mybatis.config;import java.lang.annotation.*;@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface JLock {LockModel lockModel() default LockModel.AUTO;String[] lockKey() default {};String keyConstant() default "";long expireSeconds() default 30000L;long waitTime() default 10000L;String failMsg() default "获取锁失败,请稍后重试";
}
5-2:LockModel
package com.huan.study.mybatis.config;/*** 锁的模式*/
public enum LockModel {//可重入锁REENTRANT,//公平锁FAIR,//联锁(可以把一组锁当作一个锁来加锁和释放)MULTIPLE,//红锁REDLOCK,//读锁READ,//写锁WRITE,//自动模式,当参数只有一个.使用 REENTRANT 参数多个 REDLOCKAUTO
}
5-3:BaseAspect
package com.huan.study.mybatis.aspect;import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;import java.util.ArrayList;
import java.util.List;@Slf4j
public class BaseAspect {/*** 通过spring SpEL 获取参数** @param key 定义的key值 以#开头 例如:#user* @param parameterNames 形参* @param values 形参值* @param keyConstant key的常亮* @return*/public List<String> getValueBySpEL(String key, String[] parameterNames, Object[] values, String keyConstant) {List<String> keys = new ArrayList<>();if (!key.contains("#")) {String s = "redis:lock:" + key + keyConstant;log.debug("lockKey:" + s);keys.add(s);return keys;}//spel解析器ExpressionParser parser = new SpelExpressionParser();//spel上下文EvaluationContext context = new StandardEvaluationContext();for (int i = 0; i < parameterNames.length; i++) {context.setVariable(parameterNames[i], values[i]);}Expression expression = parser.parseExpression(key);Object value = expression.getValue(context);if (value != null) {if (value instanceof List) {List value1 = (List) value;for (Object o : value1) {addKeys(keys, o, keyConstant);}} else if (value.getClass().isArray()) {Object[] obj = (Object[]) value;for (Object o : obj) {addKeys(keys, o, keyConstant);}} else {addKeys(keys, value, keyConstant);}}log.info("表达式key={},value={}", key, keys);return keys;}private void addKeys(List<String> keys, Object o, String keyConstant) {keys.add("redis:lock:" + o.toString() + keyConstant);}
}
5-4:DistributedLockHandler
package com.huan.study.mybatis.aspect;import com.huan.study.mybatis.config.JLock;
import com.huan.study.mybatis.config.LockModel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** 分布式锁解析器*/
@Slf4j
@Aspect
@Component
public class DistributedLockHandler extends BaseAspect {@Autowired(required = false)private RedissonClient redissonClient;/*** 切面环绕通知** @param joinPoint* @param jLock* @return Object*/@SneakyThrows@Around("@annotation(jLock)")public Object around(ProceedingJoinPoint joinPoint, JLock jLock) {Object obj = null;log.info("进入RedisLock环绕通知...");RLock rLock = getLock(joinPoint, jLock);boolean res = false;//获取超时时间long expireSeconds = jLock.expireSeconds();//等待多久,n秒内获取不到锁,则直接返回long waitTime = jLock.waitTime();//执行aopif (rLock != null) {try {if (waitTime == -1) {res = true;//一直等待加锁rLock.lock(expireSeconds, TimeUnit.MILLISECONDS);} else {res = rLock.tryLock(waitTime, expireSeconds, TimeUnit.MILLISECONDS);}if (res) {obj = joinPoint.proceed();} else {log.error("获取锁异常");}} finally {if (res) {rLock.unlock();}}}log.info("结束RedisLock环绕通知...");return obj;}@SneakyThrowsprivate RLock getLock(ProceedingJoinPoint joinPoint, JLock jLock) {String[] keys = jLock.lockKey();if (keys.length == 0) {throw new RuntimeException("keys不能为空");}String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());Object[] args = joinPoint.getArgs();LockModel lockModel = jLock.lockModel();RLock rLock = null;String keyConstant = jLock.keyConstant();if (lockModel.equals(LockModel.AUTO)) {if (keys.length > 1) {lockModel = LockModel.REDLOCK;} else {lockModel = LockModel.REENTRANT;}}if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");}switch (lockModel) {case FAIR:rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));break;case REDLOCK:List<RLock> rLocks = new ArrayList<>();for (String key : keys) {List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);for (String s : valueBySpEL) {rLocks.add(redissonClient.getLock(s));}}RLock[] locks = new RLock[rLocks.size()];int index = 0;for (RLock r : rLocks) {locks[index++] = r;}rLock = new RedissonRedLock(locks);break;case MULTIPLE:rLocks = new ArrayList<>();for (String key : keys) {List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);for (String s : valueBySpEL) {rLocks.add(redissonClient.getLock(s));}}locks = new RLock[rLocks.size()];index = 0;for (RLock r : rLocks) {locks[index++] = r;}rLock = new RedissonMultiLock(locks);break;case REENTRANT:List<String> valueBySpEL = getValueBySpEL(keys[0], parameterNames, args, keyConstant);//如果spel表达式是数组或者LIST 则使用红锁if (valueBySpEL.size() == 1) {rLock = redissonClient.getLock(valueBySpEL.get(0));} else {locks = new RLock[valueBySpEL.size()];index = 0;for (String s : valueBySpEL) {locks[index++] = redissonClient.getLock(s);}rLock = new RedissonRedLock(locks);}break;case READ:rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).readLock();break;case WRITE:rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).writeLock();break;}return rLock;}
}
相关文章:

分布式锁redisson
1:pom.xml添加依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.21.1</version> </dependency>2-1:方法一:读取默认ym…...

将小爱音箱接入 ChatGPT 和豆包ai改造成专属语音助手
这个GitHub项目,mi-gpt,旨在将小爱音箱和米家设备与ChatGPT和豆包集成,有效地将这些设备转变为个性化语音助手。以下是对其功能和设置的详细分析: 主要特点 角色扮演:该项目允许小爱适应不同的角色,如伴侣…...

短网址生成原理及使用
生成短网址介绍: 一、定义 短网址(Short URL)是形式上比较短的网址,它通过将原始冗长的网址进行缩短,方便用户分享和记忆。短网址的生成主要依赖于特定的算法和服务,通过后端服务转向来实现网址的缩短。 …...
C#调用word组件转pdf,遇到视图保护解决方法
由于我们在自己项目里常常要调用office组件将word另存pdf格式,但是常遇到用户上传的word视图保护, 组件不能正常打开word而导致不能有效转pdf(原因是文件被WPS编辑过),困扰很长时间,各种方法用过如用第三方组件替换office组件&…...

NAT端口映射,实现外网访问内网服务器
目录 前言一、搭建网络拓扑1.1 配置server和pc1.1.1 配置server01.1.2 配置server11.1.3 配置pc0 1.2 配置客户路由器1.2.1 配置路由器IP1.2.2 配置静态路由 1.3 配置ISP路由器 二、配置端口映射2.1 在客户路由器配置端口映射2.2 测试公网计算机访问私网服务器2.2.1 PC0向serve…...

【面试笔记】嵌入式软件工程师,汽车电子软件相关
文章目录 1. C语言基础1.1 const1.2 static1.3 回调函数的用法1.4 宏定义1.5 编译、链接过程1.6 堆与栈的区别?1.7 简单的字符串算法题,C语言实现1.7.1 给定一个字符串,按顺序筛选出不重复的字符组成字符串,输出该字符串1.7.2 给定…...

uniapp小程序开发 | 从零实现一款影视类app (后台接口实现,go-zero微服务的使用)
uniapp小程序开发实战系列,完整介绍从零实现一款影视类小程序。包含小程序前端和后台接口的全部完整实现。系列连载中,喜欢的可以点击收藏。 该篇着重介绍获取轮播图后台接口和获取正在热映电影的两个后台接口的实现。 后台服务使用golang,…...

【C#】委托
文章目录 委托自定义委托模板方法(工厂模式回调(callback)函数(观察者模式多播(multicast)委托委托的高级使用使用接口 重构 模板方法代码注意参考 委托 委托(delegate)是一种类型,定义了一种方…...

【面试题】创建两个线程交替打印100以内数字(一个打印偶数一个打印奇数)
阅读导航 一、问题概述二、解决思路三、代码实现四、代码优化 一、问题概述 面试官:C多线程了解吗?你给我写一下,起两个线程交替打印0~100的奇偶数。就是有两个线程,一个线程打印奇数另一个打印偶数,它们交替输出&…...
PgMP考试结束后多久出成绩?附成绩查询方法
PgMP考试结束后多久出成绩?这是许多参加PgMP考试的考生都非常关心的问题。今天就给大家讲解一下PgMP考试多久可以知道成绩? 一、PgMP考试成绩查询时间 PgMP考试一般在考试结束后的6-8周左右才会出成绩,届时PMI官方会通过电子邮件的形式提醒…...
springboot项目Redis统计在线用户
springboot项目Redis统计在线用户 我的项目有个显示用户的遗忘曲线,需要统计在线用户以计算他们的曲线 思考了两种方案,但都是用Redis的bitmap数据结构Bitmap是一种特殊类型的数组,其中每个元素只能存储0或1。在Redis中,Bitmap实际…...

GNeRF论文理解
文章目录 主要解决什么问题?结构设计以及为什么有效果?个人想法。 主要解决什么问题? 本文主要想要解决的问题是 如何使用uncalibrated的照片来进行Nerf重建。虽然说现在已经有了一些方式可以对相机位姿进行估计和优化,但是他们限…...

0531作业 链表
结果 整体代码 主要实现 /**实现* */ #include "./linklist.h"linklist* create_linklist(datatype param){linklist* node(linklist*)malloc(sizeof(linklist));if(NULLnode){puts("节点创建失败");}node->paramparam;node->pnextNULL;puts("…...

C++ STL - 容器
C STL(标准模板库)中的容器是一组通用的、可复用的数据结构,用于存储和管理不同类型的数据。 目录 零. 简介: 一 . vector(动态数组) 二. list(双向链表) 三. deque(…...
AI生成沉浸式3D世界(空间照片/视频)
面向Vision Pro等空间计算设备的产品指南 & 技术实现路径 一、通俗理解 ldi3格式概览:这是一种创新的3D内容格式,专为Vision Pro、Quest等VR头戴设备设计,让你能沉浸在一个几可乱真的三维世界,体验仿佛亲临其境的感受。 内容创作:利用开源工具,结合多角度摄像捕捉,…...

【Vue】异步更新 $nextTick
文章目录 一、引出问题二、解决方案三、代码实现 一、引出问题 需求 编辑标题, 编辑框自动聚焦 点击编辑,显示编辑框让编辑框,立刻获取焦点 即下图上面结构隐藏,下面结构显示,并且显示的时候让它自动聚焦。 代码如下 问题 “…...
【uCOS-III-编程指南】
uCOS-III-编程指南 ■ [野火]uCOS-III内核实现与应用开发实战指南■■■■ ■ [野火]uCOS-III内核实现与应用开发实战指南 添加链接描述 ■ ■ ■ ■...

2004NOIP普及组真题 2. 花生采摘
线上OJ: 【04NOIP普及组】花生采摘 核心思想: 1、本题为贪心即可。 2、因为本题严格限制了顺序,所以先把每个节点的花生数量按降序排序。然后逐一判断下一个花生是否需要去采摘即可 3、每一次采摘完,记录耗时 t 以及采集的花…...

SAP-SD-21-定义用于定价补充的定价过程
图9 维护条件类型...
Android AAudio——C API创建AudioTrack(六)
虽然 AAudio 试图提供一种直接的硬件访问途径,但在某些场景下,如处理兼容性问题、使用系统服务(如 AudioFlinger)或者在某些设备上,使用 AudioTrack 可能是最有效或最合适的途径。这并不违背 AAudio 的初衷,因为它的目标是提供高性能的音频处理,而不是避免使用系统服务。…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...