分布式锁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 的初衷,因为它的目标是提供高性能的音频处理,而不是避免使用系统服务。…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
