注解方式优雅的实现 Redisson 分布式锁

1前言

日常开发中,难免遇到一些并发的场景,为了保证接口执行的一致性,通常采用加锁的方式,因为服务是分布式部署模式,本地锁Reentrantlock和Synchnorized这些就先放到一边了,Redis的setnx锁存在无法抱保证原子性的问题就暂时搁且到一边,直接上大招Redisson也是我最近开发项目中基本都在用的缓存,并且也都是用它的分布式锁机制。

2Redisson分布式锁常规使用

关于Redisson的一些基本概念,本章就不做太详细的说明了,有兴趣的小伙伴可以自己去了解下,主要说下加锁的常规使用,Redisson分布式锁是基于Redis的Rlock锁,实现了JavaJUC包下的Lock接口。

Lock

public void getLock(){
    //获取锁
    RLock lock = redisson.getLock("Lxlxxx_Lock");
    try {
        // 2.加锁
        lock.lock();

    } catch (InterruptedException e) {
        e.getStackTrace();
    } finally {
        // 3.解锁
        lock.unlock();
        System.out.println("Finally,释放锁成功");
    }

getLock获取锁,lock.lock进行加锁,会出现的问题就是lock拿不到锁一直等待,会进入阻塞状态,显然这样是不好的。
TryLock
返回boolean类型,和Reentrantlock的tryLock是一个意思,尝试获取锁,获取到就返回true,获取失败就返回false,不会使获不到锁的线程一直处于等待状态,返回false可以继续执行下面的业务逻辑,当然Ression锁内部也涉及到watchDog看门狗机制,主要作用就是给快过期的锁进行续期,主要用途就是使拿到锁的有限时间让业务执行完,再进行锁释放。

RLock lock = redisson.getLock(name);
try {

    if (lock.tryLock(2, 10, TimeUnit.SECONDS)) {
        //执行业务逻辑
    } else {
        System.out.println("已存在");
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}finally {
//判断当前线程持有的锁是不是处于锁定状态,锁定状态再进行释放
    if (this.redissonLock.isHeldByCurrentThread(lockName)) {
        this.redissonLock.unlock(lockName);
    }
}

3自定义注解实现锁机制

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DistributedLock {
    String key() default "";

    int leaseTime() default 10;

    boolean autoRelease() default true;

    String errorDesc() default "系统正常处理,请稍后提交";

    int waitTime() default 1;
}

切面类实现

@Aspect
@Component
public class DistributedLockHandler {
    private static final Logger log = LoggerFactory.getLogger(DistributedLockHandler.class);
    @Autowired
    RedissonLock redissonLock;

    public DistributedLockHandler() {
    }

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        String lockName = this.getRedisKey(joinPoint, distributedLock);
        int leaseTime = distributedLock.leaseTime();
        String errorDesc = distributedLock.errorDesc();
        int waitTime = distributedLock.waitTime();

        Object var8;
        try {
            boolean lock = this.redissonLock.tryLock(lockName, (long)leaseTime, (long)waitTime);
            if (!lock) {
                throw new RuntimeException(errorDesc);
            }

            var8 = joinPoint.proceed();
        } catch (Throwable var12) {
            log.error("执行业务方法异常", var12);
            throw var12;
        } finally {
            if (this.redissonLock.isHeldByCurrentThread(lockName)) {
                this.redissonLock.unlock(lockName);
            }

        }

        return var8;
    }


    /**
     *  获取加锁的key
     * @param joinPoint
     * @param distributedLock
     * @return
     */
    private String getRedisKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        String key = distributedLock.key();
        Object[] parameterValues = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = nameDiscoverer.getParameterNames(method);
        if (StringUtils.isEmpty(key)) {
            if (parameterNames != null && parameterNames.length > 0) {
                StringBuffer sb = new StringBuffer();
                int i = 0;

                for(int len = parameterNames.length; i < len; ++i) {
                    sb.append(parameterNames[i]).append(" = ").append(parameterValues[i]);
                }

                key = sb.toString();
            } else {
                key = "redissionLock";
            }

            return key;
        } else {
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            if (parameterNames != null && parameterNames.length != 0) {
                EvaluationContext evaluationContext = new StandardEvaluationContext();

                for(int i = 0; i < parameterNames.length; ++i) {
                    evaluationContext.setVariable(parameterNames[i], parameterValues[i]);
                }

                try {
                    Object expressionValue = expression.getValue(evaluationContext);
                    return expressionValue != null && !"".equals(expressionValue.toString()) ? expressionValue.toString() : key;
                } catch (Exception var13) {
                    return key;
                }
            } else {
                return key;
            }
        }
    }
}

具体使用
在这里插入图片描述方法头加自定义注解,key参数代表需要加锁的key,errorDesc获取锁失败提示报错信息。

这边我将项目通过修改端口启动了两个服务,分别是8460和8461
在这里插入图片描述
在这里插入图片描述
通过postman调用这两个服务,模拟两个服务同时获取一把锁的场景,其中一个服务拿到锁,另外一个服务获取锁失败。
在这里插入图片描述

可以看到端口8460服务先拿到锁,8461服务tryLock获取锁失败,实现了加锁逻辑。
在这里插入图片描述
在这里插入图片描述
4总结
分布式锁的使用场景还是需要多注意下,根据业务场景来,并发量不大的情况下,其实没有必要加,可能在移动端操作比较频繁的情况下需要注意并发,目前我做的b端项目,通过简单接口幂等性操作就可以避免重复提交,切勿不要盲目加锁,多少会影响一些性能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

asp.net健身会所管理系统sqlserver

asp.net健身会所管理系统sqlserver说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql server数据库 功能模块&#xff1a; 首页 会员注册 教练预约 系统公告 健身课程 在线办卡 用户中心[修改个人信息 修…

Modbus RTU 使用教程(modbus教程、modbus协议)

参考文章&#xff1a;这节课带你吃透Modbus通信协议 文章目录 Modbus RTU 使用教程第1部分&#xff1a;Modbus RTU 协议概述什么是Modbus RTU协议Modbus RTU的特点工作原理 第2部分&#xff1a;硬件和软件要求硬件要求软件要求 第3部分&#xff1a;Modbus RTU数据结构数据帧格式…

android适配鸿蒙系统开发

将一个Android应用迁移到鸿蒙系统需要进行细致的工作&#xff0c;因为两者之间存在一些根本性的差异&#xff0c;涉及到代码、架构、界面等多个方面的修改和适配。以下是迁移工作可能涉及的一些主要方面&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专…

各品牌PLC元件在modbus内区域

1台达&#xff1a; 输出在0区&#xff0c; 040961是在 0区 0xA000~0xA0FF 【Y0~Y377】 输入在1区&#xff0c;124577是在 1区 0x6000~0x60FF 【X0~X377】 M寄存器0区&#xff0c;0000001是 0区&#xff0c;0x000~0x1FFF 【M0~M8191】 D寄存器4区&#xff0c;400000…

CTFhub-RCE-过滤运算符

检查网页源代码发现如果用户输入 || &#xff08;逻辑或操作符&#xff09; 或者是 & &#xff08;按位与操作符&#xff09;&#xff0c;就不会执行。直接进行测试&#xff0c;在 URL 后输入本地 IP 进行 ping 命令测试&#xff0c;发现可以正常回显。检查网页源代码发现如…

2018年五一杯数学建模B题商业银行人民币贷款规模分配及盈利问题解题全过程文档及程序

2019年五一杯数学建模 B题 商业银行人民币贷款规模分配及盈利问题 原题再现 商业银行贷款投放的简单模型是&#xff1a;从客户端吸收存款&#xff0c;缴存法定准备金&#xff08;法定准备金率&#xff1a;大型金融机构15.5%&#xff0c;中小金融机构12%&#xff1b;法定准备金…

Spring Boot Actuator:自定义端点

要在Spring Boot Actuator中实现自定义端点&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.创建一个自定义端点类 该类需要使用Endpoint注解进行标记&#xff0c;并使用Component注解将其作为Spring Bean进行管理。 package com.example.highactuator.point;import lo…

flowable消息事件

一&#xff1a;启动事件 定义消息。 引用消息。 <startEvent id"msgStart" name"消息启动事件" isInterrupting"true"><messageEventDefinition messageRef"myMsgStart"></messageEventDefinition> </startE…

机器学习第6天:线性回归模型正则化

文章目录 机器学习专栏 正则化介绍 岭回归 岭回归成本函数 核心代码 示例 Lasso回归 Lasso回归损失函数 核心代码 弹性网络 弹性网络成本函数 核心代码 结语 机器学习专栏 机器学习_Nowl的博客-CSDN博客 正则化介绍 作用&#xff1a;正则化是为了防止模型过拟合…

es使用客户端,“grunt” 不是内部或外部命令,多种解决方法

”grunt“不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 4、问题排查 查看node的安装根目录 npm root -g 在运行grunt -version还是不行 网上找了很多&#xff0c;给出正确解决方案的没几个&#xff0c;所以自己摸索&#xff0c;最后确定了加环境变量的解…

Python实验项目7 :tkinter GUI编程

&#xff08;1&#xff09;利用tkinter 制作界面&#xff0c;效果图如下&#xff1a; from tkinter import * # winTk() for i in range(1,20):Button(width5,height10,bg"black" if i%20 else"white").pack(side"left") win.geometry("8…

Android 屏幕适配

目录 一、为什么要适配 二、几个重要的概念 2.1 屏幕尺寸 2.2 屏幕分辨率 2.3 屏幕像素密度 2.4 屏幕尺寸、分辨率、像素密度三者关系 三、常用单位 3.1 密度无关像素(dp) 3.2 独立比例像素&#xff08;sp&#xff09; 3.3 dp与px的转换 四、解决方案 4.1 今日头条…

Docker之DockerFile解析

DockerFile解析 是什么 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 概述 官网 https://docs.docker.com/engine/reference/builder/ 构建三步骤 编写Dockerfile文件 docker build命令构建镜像 docker run依镜像运…

李沐的学习Pytorch环境配置

https://github.com/Miraclelucy/dive_into_deep_learning/tree/main 上面是别人的笔记 可以学一下。 如果没有梯子&#xff0c;按照清华源配置 清华源conda配置 最好下载 1.11版本torch那一套 然后装d2l版本可以装 pip install d2l0.17.6然后可以用 http://localhost:8889/…

DolphinScheduler V3.1.8 海豚调度器【概述、安装、功能介绍、项目运用、邮箱预警设置】轻松拿捏!

概述 Apache DolphinScheduler 是一个分布式易扩展的可视化 DAG 工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处理过程的解决方案。 Apache DolphinScheduler 旨在解决复杂的大数据任务依赖关系&#xff0c;并为应…

Diffusion Models CLIP

Introduction to Diffusion Models 生成模型 主要指的是无监督学习中的生成模型&#xff0c;在无监督学习中的主要任务是让机器学习给定的样本&#xff0c;然后生成一些新的东西出来。比如&#xff1a;给机器看一些图片&#xff0c;能够生成一些新的图片出来&#xff0c;给机器…

.locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 随着科技的不断发展&#xff0c;网络空间的威胁也日益增加。其中&#xff0c;.locked勒索病毒作为一种恶意软件&#xff0c;给用户的数据安全带来了极大的威胁。本文91数据恢复将介绍.locked勒索病毒的特点&#xff0c;以及如何有效恢复被它加密的数据文件&a…

Java入门篇 之 多态

本篇碎碎念&#xff1a;个人认为&#xff0c;一切都不是不学习的理由&#xff0c;在如今这个"内卷"的时代&#xff0c;唯有认真学习&#xff0c;掌握知识&#xff0c;才能把握命运 今日份励志文案:你必须拼尽全力&#xff0c;才有资格说运气不好 加油吧&#xff0c;少…

Elasticsearch基础条件查询

条件查询 query&#xff1a;查询 match&#xff1a;匹配 match_all&#xff1a;匹配所有 #第一种 GET /shopping/_search?q名字:张三#第二种 GET /shopping/_search {"query": {"match": {"名字": "张三"}} }#全量查询 match_all G…

MIB 6.1810实验Xv6 and Unix utilities(5)find

难度:moderate Write a simple version of the UNIX find program for xv6: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c. 题目要求&#xff1a;实现find &#xff0c;即在某个路径中&#xff0c;找出某…