基于Redisson实现分布式锁

基于redisson实现分布式锁

之前背过分布式锁几种实现方案的八股文,但是并没有真正自己实操过。现在对AOP有了更深一点的理解,就自己来实现一遍。

1、分布式锁的基础知识

分布式锁是相对于普通的锁的。普通的锁在具体的方法层面去锁,单体应用情况下,各个进入的请求都只能进入到一个应用里面,也就能达到锁住方法的效果。
而分布式的系统,将一个项目部署了多个实例,通过nginx去做请求转发将请求分到各个实例。不同实例之间共用代码,共用数据库这些。比如一个请求进入A实例,获得了锁;如果继续有请求进入A实例,则会排队等待。但如果请求进入的是B实例呢?B实例的锁和A实例没有关系,那么进入B实例的请求也会获取到锁,然后进入方法。这样锁的作用就没有达到。这种情况下,就引出了分布式锁,这是专门为了解决分布式系统的并发问题的。做法是让不同的实例都能使用同一个锁。比如redis,redis内部是单线程的,把锁放在redis,这样就可以多个实例共用一个锁。

2、Redisson

Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。
利用Redisson可以很轻松就实现分布式锁。

3、实现的几个点

如何动态将拼接key所需的参数值传入到切面?
有两种方案,一种是通过给参数加上注解来标识那些参数是作为key使用的。在切面内通过获取方法的参数上的注解来获取所需的参数值。
另一种是通过定义key的参数名,在切面获取方法参数的参数名进行对比,一样的就是所需的参数。
获取锁失败如何重试?
Redisson提供了重试的能力,以及重试等待时长等。

4、代码实现

前置操作:项目引入Redis

引入Redisson依赖

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version></dependency>

项目yaml文件

spring:#reids配置redis:database: 1host: 127.0.0.1port: 6379# redisson配置redisson:# 数据库编号database: 1# 节点地址address: redis://127.0.0.1:6379

编写Redisson配置文件

package com.yumoxuan.myapp.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {@Value("${spring.redis.redisson.address}")public String address;@Value("${spring.redis.redisson.database}")public Integer database;@Beanpublic RedissonClient getRedissonClient(){Config config=new Config();config.useSingleServer().setAddress(address).setDatabase(database);return Redisson.create(config);}
}

定义分布式锁注解

package com.yumoxuan.myapp.core.aspect.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {/*** 锁的名称* @return*/String name() default "";/*** 锁的参数key* @return*/String[] key() default {};/*** 锁过期时间* @return*/int expireTime() default 30;/*** 锁过期时间单位* @return*/TimeUnit timeUnit() default TimeUnit.SECONDS;/*** 等待获取锁超时时间* @return*/int waitTime() default 10;
}

切面实现

package com.yumoxuan.myapp.core.aspect;
import com.yumoxuan.myapp.core.aspect.annotation.DistributedLock;
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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Slf4j
@Aspect
@Component
public class DistributedLockAspect {@Resourcepublic RedissonClient redissonClient;@Pointcut("@annotation(com.yumoxuan.myapp.core.aspect.annotation.DistributedLock)")public void pointcut() {}@Around("pointcut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();DistributedLock annotation = method.getAnnotation(DistributedLock.class);String name = annotation.name();String key = name;String[] keys = annotation.key();int expireTime = annotation.expireTime();TimeUnit timeUnit = annotation.timeUnit();int waitTime = annotation.waitTime();Object[] args = point.getArgs(); // 参数值String[] paramNames = signature.getParameterNames(); // 参数名for (int i = 0; i < args.length; i++) {String paramName = paramNames[i];for (int j = 0; j < keys.length; j++) {if (paramName.equals(keys[j])) {key = key + ":" + args[i];}}}log.info(Thread.currentThread().getId()+" "+"key:"+key);RLock lock = redissonClient.getLock(key);boolean res = false;Object obj = null;try {if (waitTime == -1) {res = true;lock.lock(expireTime, timeUnit);} else {res = lock.tryLock(waitTime, expireTime, timeUnit);}if (res) {obj = point.proceed();} else {log.error("分布式锁获取异常");}} finally {//释放锁if (res) {lock.unlock();}}return obj;}
}

验证效果

	@ApiOperation(value="my_app_user-添加", notes="my_app_user-添加")@PostMapping(value = "/add")@DistributedLock(name = "添加分布式锁",key = {"userId"})public Result<?> add(@RequestBody MyAppUser myAppUser, String userId) {log.info(Thread.currentThread().getId()+" "+new Date());try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}return Result.ok();}

如下图,76.77的key是一样的,从打印的时间上看,两次打印时间相差了5秒,说明后者被阻塞。而68的线程key和另外两个不一样,所以没有被阻塞,在76和77的中间就运行了。符合分布式锁的效果。
在这里插入图片描述

5、拓展

要实现一个功能完善的分布式锁,其实还有很多内容。
比如锁要过期了,但事务未执行完毕,则需要进行锁续期,这个可以通过守护线程实现。

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

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

相关文章

班迪录屏(Bandicam)7.0下载以及安装教程

最近有小伙伴私信我&#xff0c;问我有没有好用的录屏工具&#xff0c;今天给大家分享一个我一直在使用的录屏工具&#xff0c;也是解锁了V1P版本&#xff0c;绿色版打开就可以使用~ Bandicam录屏&#xff08;PC&#xff09; Bandicam录屏是一款专为捕捉屏幕精彩瞬间而设计的…

PHP家政服务预约单开版微信小程序系统源码

&#x1f3e0; —— 便捷生活&#xff0c;从指尖开始&#x1f4aa; &#x1f308;【开篇&#xff1a;家政新风尚&#xff0c;一键触达】 在忙碌的生活节奏中&#xff0c;你是否渴望拥有一个温馨、整洁的家&#xff0c;却又苦于找不到合适的家政服务&#xff1f;现在&#xff…

EXTI寄存器,AFIO的简洁,EXTI配置的流程

一&#xff0c;AFIO简介 AFIO是Alternate Function Input/Output 的缩写&#xff0c;表示复用功能IO&#xff0c;主要用于实现IO端口的复用功能以及外部中断的控制 STM32外设有很多I/O以及内置外设&#xff08;如12C&#xff0c;ADC,ISP,USART等&#xff09;。为节省引出管脚的…

初试成绩占比百分之70!计算机专硕均分340+!华中师范大学计算机考研考情分析!

华中师范大学&#xff08;Central China Normal University&#xff09;简称“华中师大”或“华大”&#xff0c;位于湖北省会武汉&#xff0c;是中华人民共和国教育部直属重点综合性师范大学&#xff0c;国家“211工程”、“985工程优势学科创新平台”重点建设院校&#xff0c…

PhysioLLM 个性化健康洞察:手表可穿戴设备实时数据 + 大模型

个性化健康洞察&#xff1a;可穿戴设备实时数据 大模型 提出背景PhysioLLM 图PhysioLLM 实现数据准备用户模型和洞察生成个性化数据总结和洞察是如何生成的&#xff1f; 解析分析 提出背景 论文&#xff1a;https://arxiv.org/pdf/2406.19283 虽然当前的可穿戴设备伴随应用&…

最近,被“AI”狠狠刷屏了......

最近&#xff0c;被“AI”狠狠刷屏了。 作为时下最热门的话题&#xff0c;AI画图、AI配音、AI写文案、AI做视频......AI在最近两年可谓是火遍全球。ChatGPT、Midjourney和SORA等技术不断涌现&#xff0c;不仅深刻改变着我们的生活方式&#xff0c;也推动了AI技术的飞速发展。 …

vue使用scrollreveal和animejs实现页面滑动到指定位置后再开始执行动画效果

效果图 效果链接&#xff1a;http://website.livequeen.top 介绍 一、Scrollreveal ScrollReveal 是一个 JavaScript 库&#xff0c;用于在元素进入/离开视口时轻松实现动画效果。 ScrollReveal 官网链接&#xff1a;ScrollReveal 二、animejs animejs是一个好用的动画库…

nvm安装以及idea下vue启动项目过程和注意事项

注意1&#xff1a;nvm版本不要太低&#xff0c;1.1.7会出现下面这个问题&#xff0c;建议1.1.10及其以上版本 然后安装这个教程安装nvm和node.js 链接: nvm安装教程&#xff08;一篇文章所有问题全搞定&#xff0c;非常详细&#xff09; 注意2&#xff1a;上面的教程有一步骤…

实现各平台确定性的物理碰撞

1.使用FixedUpdate而不是Update 1.物理运算&#xff0c;比如刚体运动系统的运算是通过固定的时间来驱动的。 2.再moba帧同步游戏中&#xff0c;15帧的固定调用差不多是网络那边的极限了&#xff0c;采用其他手段如平滑显示来提高画面的平滑度。 FixedUpdate是以一个固定的帧率…

开始尝试从0写一个项目--前端(一)

基础项目构建 创建VUE初始工程 确保自己下载了node.js和npm node -v //查看node.js的版本 npm -v //查看npm的版本 npm i vue/cli -g //安装VUE CLI 创建 以管理员身份运行 输入&#xff1a;vue ui 就会进入 点击创建 自定义项目名字&#xff0c;选择npm管理 结…

“工控机”是什么?和普通电脑有区别嘛!

在现代工业生产中,有一种特殊的计算机,它不像普通电脑那样被放置于明亮的办公室内,而是常常藏身于机器轰鸣、环境恶劣的工厂车间里,这就是工控机——工业控制计算机的简称。作为工业自动化领域不可或缺的核心设备,工控机不仅承载着监控与数据采集(SCADA)、过程控制、数据…

概率论与数理统计_下_科学出版社

contents 前言第5章 大数定律与中心极限定理独立同分布中心极限定理 第6章 数理统计的基本概念6.1 总体与样本6.2 经验分布与频率直方图6.3 统计量6.4 正态总体抽样分布定理6.4.1 卡方分布、t 分布、F 分布6.4.2 正态总体抽样分布基本定理 第7章 参数估计7.1 点估计7.1.1 矩估计…

上海-灵曼科技(面经)

上海-灵曼科技 hr电话面 个人简介 个人信息的询问 是否知道芋道框架 技术面 算法题 14. 最长公共前缀&#xff08;写出来即可&#xff09; 聊一下Docker Docker核心概念总结Docker实战 聊一下AOP Spring AOP详解 聊一下JWT JWT 基础概念详解JWT 身份认证优缺点分析 Spri…

使用React复刻ThreeJS官网示例——keyframes动画

最近在看three.js相关的东西&#xff0c;想着学习一下threejs给的examples。源码是用html结合js写的&#xff0c;恰好最近也在学习react&#xff0c;就用react框架学习一下。 本文参考的是threeJs给的第一个示例 three.js examples (threejs.org) 一、下载threeJS源码 通常我们…

【接口自动化测试】第四节.实现项目核心业务的单接口自动化测试

文章目录 前言一、登录单接口自动化测试 1.1 登录单接口文档信息 1.2 登录成功 1.3 登录失败&#xff08;用户名为空&#xff09;二、数据驱动的实现 2.1 json文件实现数据驱动三、课程添加单接口自动化测试 3.1 课程添加单接口文档信息 3.2 课程…

Promethuse-监控 Etcd

一、思路 Prometheus监控Etcd集群&#xff0c;是没有对应的exporter&#xff0c;而 由CoreOS公司开发的Operator&#xff0c;用来扩展 Kubernetes API&#xff0c;特定的应用程序控制器&#xff0c;它用来创建、配置和管理复杂的有状态应用&#xff0c;如数据库、缓存和监控系…

软件性能测试有哪几种测试方法?专业性能测试报告出具

软件性能测试是指对软件系统在特定负载条件下的性能进行评估和验证的过程&#xff0c;目的是确保软件在正常使用的情况下能够满足用户的要求&#xff0c;并在稳定的性能水平下运行&#xff0c;在软件开发过程中起到了至关重要的作用&#xff0c;可以确保软件产品的质量和可靠性…

【Android面试八股文】你是怎么保证Android设备的时间与服务器时间同步的?(使用NTP和TrueTime方案)

文章目录 一、网络时间协议(NTP)二、使用网络时间协议(NTP)2.1 使用系统提供的 NTP 服务器2.2 使用TrueTime2.2.1 引入TrueTime库2.2.2 初始化 TrueTime2.2.3 用法2.2.4 使用 TrueTime 获取时间2.2.4 自动更新时间2.2.5 注意事项二. 使用 HTTP 请求获取服务器时间2.1. 发送…

【unity实战】使用Unity实现动作游戏的攻击 连击 轻重攻击和打击感

最终效果 文章目录 最终效果前言素材下载&#xff1a;玩家移动跳跃控制攻击动画配置轻攻击重攻击 攻击时禁止移动和攻击移动补偿敌人击退和播放受击动画受击特效攻击停顿和屏幕震动局部顿帧&#xff08;补充&#xff09;参考源码完结 前言 注意本文为自己的学习记录笔记&#…

Android平台崩溃和 ANR 问题进行符号化解析、解析崩溃日志的内存地址

使用Android Logcat Stacktrace Utility | Android Logcat | 1.2.3 1.设置so库路径 2.打开Stacktrace Utility工具 3.在Original粘贴报错内存地址 4.点击Resolve Stacktraces,就会解析出内存地址 如果是红色,解析失败了,缺少原生so库,可以在第一步添加so库文件再次尝试…