当前位置: 首页 > news >正文

分布式锁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&#xff1a;pom.xml添加依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.21.1</version> </dependency>2-1&#xff1a;方法一&#xff1a;读取默认ym…...

将小爱音箱接入 ChatGPT 和豆包ai改造成专属语音助手

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

短网址生成原理及使用

生成短网址介绍&#xff1a; 一、定义 短网址&#xff08;Short URL&#xff09;是形式上比较短的网址&#xff0c;它通过将原始冗长的网址进行缩短&#xff0c;方便用户分享和记忆。短网址的生成主要依赖于特定的算法和服务&#xff0c;通过后端服务转向来实现网址的缩短。 …...

C#调用word组件转pdf,遇到视图保护解决方法

由于我们在自己项目里常常要调用office组件将word另存pdf格式&#xff0c;但是常遇到用户上传的word视图保护&#xff0c; 组件不能正常打开word而导致不能有效转pdf(原因是文件被WPS编辑过)&#xff0c;困扰很长时间&#xff0c;各种方法用过如用第三方组件替换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 堆与栈的区别&#xff1f;1.7 简单的字符串算法题&#xff0c;C语言实现1.7.1 给定一个字符串&#xff0c;按顺序筛选出不重复的字符组成字符串&#xff0c;输出该字符串1.7.2 给定…...

uniapp小程序开发 | 从零实现一款影视类app (后台接口实现,go-zero微服务的使用)

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

【C#】委托

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

【面试题】创建两个线程交替打印100以内数字(一个打印偶数一个打印奇数)

阅读导航 一、问题概述二、解决思路三、代码实现四、代码优化 一、问题概述 面试官&#xff1a;C多线程了解吗&#xff1f;你给我写一下&#xff0c;起两个线程交替打印0~100的奇偶数。就是有两个线程&#xff0c;一个线程打印奇数另一个打印偶数&#xff0c;它们交替输出&…...

PgMP考试结束后多久出成绩?附成绩查询方法

PgMP考试结束后多久出成绩&#xff1f;这是许多参加PgMP考试的考生都非常关心的问题。今天就给大家讲解一下PgMP考试多久可以知道成绩&#xff1f; 一、PgMP考试成绩查询时间 PgMP考试一般在考试结束后的6-8周左右才会出成绩&#xff0c;届时PMI官方会通过电子邮件的形式提醒…...

springboot项目Redis统计在线用户

springboot项目Redis统计在线用户 我的项目有个显示用户的遗忘曲线&#xff0c;需要统计在线用户以计算他们的曲线 思考了两种方案&#xff0c;但都是用Redis的bitmap数据结构Bitmap是一种特殊类型的数组&#xff0c;其中每个元素只能存储0或1。在Redis中&#xff0c;Bitmap实际…...

GNeRF论文理解

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

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&#xff08;标准模板库&#xff09;中的容器是一组通用的、可复用的数据结构&#xff0c;用于存储和管理不同类型的数据。 目录 零. 简介&#xff1a; 一 . vector&#xff08;动态数组&#xff09; 二. list&#xff08;双向链表&#xff09; 三. deque&#xff08…...

AI生成沉浸式3D世界(空间照片/视频)

面向Vision Pro等空间计算设备的产品指南 & 技术实现路径 一、通俗理解 ldi3格式概览:这是一种创新的3D内容格式,专为Vision Pro、Quest等VR头戴设备设计,让你能沉浸在一个几可乱真的三维世界,体验仿佛亲临其境的感受。 内容创作:利用开源工具,结合多角度摄像捕捉,…...

【Vue】异步更新 $nextTick

文章目录 一、引出问题二、解决方案三、代码实现 一、引出问题 需求 编辑标题, 编辑框自动聚焦 点击编辑&#xff0c;显示编辑框让编辑框&#xff0c;立刻获取焦点 即下图上面结构隐藏&#xff0c;下面结构显示&#xff0c;并且显示的时候让它自动聚焦。 代码如下 问题 “…...

【uCOS-III-编程指南】

uCOS-III-编程指南 ■ [野火]uCOS-III内核实现与应用开发实战指南■■■■ ■ [野火]uCOS-III内核实现与应用开发实战指南 添加链接描述 ■ ■ ■ ■...

2004NOIP普及组真题 2. 花生采摘

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

SAP-SD-21-定义用于定价补充的定价过程

图9 维护条件类型...

Android AAudio——C API创建AudioTrack(六)

虽然 AAudio 试图提供一种直接的硬件访问途径,但在某些场景下,如处理兼容性问题、使用系统服务(如 AudioFlinger)或者在某些设备上,使用 AudioTrack 可能是最有效或最合适的途径。这并不违背 AAudio 的初衷,因为它的目标是提供高性能的音频处理,而不是避免使用系统服务。…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...