Redisson分布式锁使用详解

news2024/9/22 1:42:01

引言

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

为什么要使用分布式锁

在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,这个时候,便需要使用到分布式锁。

目前几乎很多大型网站及应用都是分布式部署的,如何保证分布式场景中的数据一致性问题一直是一个比较重要的话题。在某些场景下,为了保证数据的完整性和一致性,我们需要保证一个方法在同一时间内只能被同一个线程执行,这就需要使用分布式锁。

分布式锁需满足四个条件

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。

  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

  3. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了,即不能误解锁。

  4. 具有容错性。只要大多数Redis节点正常运行,客户端就能够获取和释放锁。

Redisson 分布式锁实现原理图

Redisson分布式锁常规使用 

关于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);
    }
}

自定义注解实现锁机制

通常我们都会将redisson实例注入到方法类里面,然后调用加锁方法进行加锁,如果其他业务方法也需要加锁执行,将会产生很多重复代码,由此采用AOP切面的方式,只需要通过注解的方式就能将方法进行加锁处理。

自定义注解

@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;
            }
        }
    }
}

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

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

相关文章

VBA技术资料MF178:将某个文件夹中的图片导入Word

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

敏捷开发笔记(第13章节)--COMMAND模式和ACTIVE OBJECT模式

1&#xff1a;PDF上传链接 【免费】敏捷软件开发(原则模式与实践)资源-CSDN文库 没有人天生就具有命令他人的权利。 --Denis Diderot&#xff08;1713一1784&#xff0c;法国哲学家&#xff0c;百料全书编者) 在近几…

20240724----安装git和配置git的环境变量/如何用命令git项目到本地idea

备注参考博客&#xff1a; 1&#xff09;可以参考博客&#xff0c;用git把项目git到本地 2&#xff09;可以参考博客vcs没有git 3)git版本更新&#xff0c;覆盖安装 &#xff08;一&#xff09;安装git &#xff08;1&#xff09;官网下载的链接 https://git-scm.com/downlo…

【轨物方案】新型储能管理系统(EMS)解决方案

储能EMS作为储能系统的大脑&#xff0c;其重要性不言而喻&#xff0c;但是随着储能行业的不断扩大以及对应产品的不断升级&#xff0c;其对应的售后运维变得越来越复杂&#xff0c;储能系统急需配备完善的EMS系统来监控和优化自身的运营状态&#xff0c;同时满足售后运维&#…

LeetCode 热题 HOT 100 (010/100)【宇宙最简单版】

【链表】No. 0206 反转链表 【简单】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#xf…

Docker-Compose配置zookeeper+KaFka+CMAK简单集群

1. 本地DNS解析管理 # 编辑hosts文件 sudo nano /etc/hosts # 添加以下三个主机IP 192.168.186.77 zoo1 k1 192.168.186.18 zoo2 k2 192.168.186.216 zoo3 k3注&#xff1a;zoo1是192.168.186.77的别名&#xff0c;zoo2是192.168.186.18的别名&#xff0c;zoo3是192.168.186.1…

JavaScript(14)——DOM

Web API 作用&#xff1a;就是使用JS去操作html和浏览器 分类&#xff1a;DOM&#xff08;文档对象模型&#xff09;、BOM&#xff08;浏览器对象模型&#xff09; DOM DOM是用来呈现以及与任意HTML或XML文档交互的API&#xff0c;简单来说就是操作网页的内容。 DOM树 将H…

Ansible的脚本-----playbook剧本【下】

目录 实战演练六&#xff1a;tags 模块 实战演练七&#xff1a;Templates 模块 实战演练六&#xff1a;tags 模块 可以在一个playbook中为某个或某些任务定义“标签”&#xff0c;在执行此playbook时通过ansible-playbook命令使用--tags选项能实现仅运行指定的tasks。 playboo…

secureCRT同时在所有已打开窗口执行命令、mac-os下使用的SecureCRT版本 以及 SecureCRT一段时间不操作没有响应的问题

一、secureCRT命令行工具一次性同时在所有已打开窗口执行命令 公司的服务器比较多&#xff0c;最近因为opcache&#xff0c;上线发布后&#xff0c;需要重启所有的WEB服务器上的php。目前使用的jenkins发布&#xff0c;不过账号安全问题&#xff0c;给jenkins的账号权限受限不能…

【BUG】已解决:IndexError: positional indexers are out-of-bounds

IndexError: positional indexers are out-of-bounds 目录 IndexError: positional indexers are out-of-bounds 【常见模块错误】 【解决方案】 原因分析 解决方法 示例代码 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博…

golang常用的一些 strings函数

1.判断字符串是否包含另一个字符串 strings.Contains str : "exported_provider_example"prefix : "exported_provider"if strings.Contains(str, prefix) {fmt.Println("字符串包含 exported_provider")} else {fmt.Println("字符串不包含…

学习笔记(数据结构:链表 栈)3

fun为回调函数由主函数决定 栈: 栈是限定仅在表尾进行插入和删除操作的线性表。 先进后出、后进先出 栈顶:允许操作的一端 栈底:不允许操作的一端 入栈&#xff0c;出栈。 顺序栈 链式栈 1.创建 CreateSeqStack 2.销毁 DestroySeqStack 3.判断是否为空栈 IsEm…

机器学习(二十一):错误分析、创造数据和迁移学习

一、错误分析 假设交叉验证集一共有500个数据点&#xff0c;模型拟合结果中&#xff0c;有100个数据点有误。 错误分析就是&#xff0c;手动地分析这100个错误数据&#xff08;或随机选择一些错误数据&#xff09;&#xff0c;根据它们的共同属性、共同特征分类&#xff0c;然…

一文了解论文检索中ESCI与SCI-E的关系

一、SCI与ESCI相关概念 1、SCI SCI全称为Science Citation Index, 简称 SCI 。于1957 年由美国科学信息研究所(Institute for Scientific Information, 简称 ISI)在美国费城创办&#xff0c;是由美国科学信息研究所(ISI)1961年创办出版的引文数据库。SCI收录全世界出版的数、…

深入了解 JMeter 中的响应断言

JMeter 是一个强大的性能测试工具&#xff0c;能够模拟大量用户并发访问网站或应用程序&#xff0c;以便测试其性能和稳定性。在进行性能测试时&#xff0c;我们需要对响应结果进行断言&#xff0c;以确保应用程序或网站的功能和性能符合预期。 JMeter 提供了一套丰富的响应断…

唐山养老院哪家好—安馨医康养中心,让家人放心,老人舒心

随着社会的发展和人口老龄化的加剧&#xff0c;养老问题逐渐成为社会关注的焦点。特别是在面对失能、失智和高龄老人的养老需求时&#xff0c;传统的“养儿防老”观念和家庭护理模式已经难以满足现代家庭的实际需求。因此&#xff0c;越来越多的家庭开始考虑将老人送入专业的养…

【Java】 力扣 跳跃游戏II

目录 题目链接题目描述思路代码 题目链接 45.跳跃游戏II 题目描述 思路 贪心算法&#xff1a;找到能跳到最远的 cover是记录当前能挑到的最远位置 end表示跳到的最远边界 step表示的是跳跃的次数 开始遍历i0&#xff0c;end0&#xff0c;step会step&#xff0c; 循环遍历为…

Docker搭建群晖

Docker搭建群晖 本博客介绍在docker下搭建群晖 1.编辑docker-compose.yml文件 version: "3" services:dsm:container_name: dsmimage: vdsm/virtual-dsm:latestenvironment:DISK_SIZE: "16G"cap_add:- NET_ADMIN ports:- 8080:50…

js引入和使用

ESMAScript标准 语句基础标准 DOM 针对HTML标签&#xff0c;CSS样式的语言部分 Document Object Model BOM 针对浏览器所使用的开发部分 Browser Object Model js引入 script只能写在head或者body中&#xff09;&#xff0c;如果写在html后这种写法本来就是错误的&am…

【C语言】英寸英尺转换米

运行的结果为 我们百度一下 恒明显我们的答案错了,那这个是为什么呢? 问题就出现在计算的地方,c语言规定两个整数计算,那么小数的部分会被丢弃. 如果计算的两个数中有一个数为小数,那么会将两个数都变为小数在进行计算,结果也会是小数. 那么我们现在就有解决办法了. 方法一…