简单理解下基于 Redisson 库的分布式锁机制

server/2024/11/27 23:26:56/

目录

    • 简单理解下基于 Redisson 库的分布式锁机制
      • 代码流程:
        • 方法的调用:
        • 具体锁的实现:
          • riderBalance 方法:
          • tryLock 方法(重载):
          • tryLock 方法(核心实现):

在这里插入图片描述

简单理解下基于 Redisson 库的分布式锁机制

这段代码实现了一个基于 Redisson 库的分布式锁机制

Redisson 是一个基于 Redis 的 Java 客户端库,它提供了丰富的分布式数据结构和工具,旨在使 Java 开发者能够轻松地在分布式环境下使用 Redis。

Redisson 是 Redis 的高级封装,除了常规的 Redis 命令支持外,它还提供了很多额外的功能,如分布式锁、分布式集合、分布式队列、分布式缓存等,使得开发者在分布式系统中实现高效、可靠的操作。

比方对一个用户所拥有的金额进行增减操作,肯定需要上锁才能保证一定的安全性。

代码流程:

方法的调用:

一个简单的流程:

LockUtil.riderBalance(riderId, () -> {…}) 是一个加锁操作,是一种使用分布式锁来确保线程安全的机制,确保在修改 余额时,避免并发冲突。

简要来说,它的作用是在执行余额操作时,确保只有一个线程能够对指定的骑手账户进行操作,避免多个线程同时修改余额,导致数据不一致的情况。

LockUtil.riderBalance(riderId, …) 通过 riderId 获取一个锁,这样就能确保在同一时刻,只有一个线程可以执行传入的操作逻辑。

riderId 是骑手的唯一标识,通过它来加锁,防止对同一个骑手账户的并发操作。

() -> {…}: 这是一个 Lambda 表达式,表示当获得锁之后要执行的具体操作。这个操作包括:获取骑手的余额信息、执行余额扣除、记录操作日志等步骤。

    /*** 扣除余额*/@Override@Transactional(rollbackFor = Exception.class)public boolean deductionBalance(Integer riderId, BigDecimal amount, String title, Integer orderId, String orderCode, RiderCashlogType cashlogType, RiderCashlogStatus cashlogStatus) {return LockUtil.riderBalance(riderId, () -> {// 变更前RiderServiceDto riderBefore = this.getRiderById(riderId);// 减少余额 -------- ★★★★★★★★★★★★★★★★★★★★★★★★boolean flag = xxRiderService.deductionBalance(riderId, amount);// 变更后RiderServiceDto riderAfter = this.getRiderById(riderId);// 增加流水日志if (flag) {RiderCashlogServiceDto cashlog = new RiderCashlogServiceDto();cashlog.setMoneyType(RiderCashlogMoneyType.BALANCE);cashlog.setTitle(title);......return xxRiderCashlogService.addCashlog(cashlog);}return false;}, () -> {throw BizException.newInstance(ErrorCode.BUSY);});}

调用接口方法

    /*** 扣除余额*/boolean deductionBalance(Integer riderId, BigDecimal amount);

这里再进行详细的扣除方法,依然是用同个锁

    /*** 扣除余额*/@Overridepublic boolean deductionBalance(Integer riderId, BigDecimal amount) {if (amount.compareTo(BigDecimal.ZERO) == 0) {return true;}// 加锁return LockUtil.riderBalance(riderId, () -> {if (ObjectUtil.isEmpty(riderId) || amount.compareTo(BigDecimal.ZERO) < 0) {log.error("扣除xx余额失败,riderId:{},amount:{}", riderId, amount);return false;}RiderServiceDto rider = this.getRiderById(riderId);// 获取当前账号的余额BigDecimal valid = rider.getValidMoney();// 判断当前余额是否足够if (valid.compareTo(amount) < 0) {throw BizException.newInstance(ErrorCode.ARGUMENT_ERROR, "扣除余额失败,余额小于" + amount.toPlainString());}return this.update(new UpdateWrapper<RiderEntity>().lambda().eq(RiderEntity::getId, riderId).setSql("valid_money = valid_money - " + amount));}, () -> {throw BizException.newInstance(ErrorCode.BUSY);});}
具体锁的实现:
riderBalance 方法:
    /*** 锁骑手余额------* Integer riderId: 骑手的 ID,作为分布式锁的标识。* Supplier<T> supplier: 提供数据或执行操作的函数接口。*                       传入的 Supplier<T> 函数接口,用来提供需要执行的业务操作。如果获取锁成功,业务操作会在锁内部执行。* InvokeInter lockFail: 如果获取锁失败,执行的操作。*/public static <T> T riderBalance(Integer riderId, Supplier<T> supplier, InvokeInter lockFail) {return tryLock(RedisKey.LOCK_RIDER_BALANCE_XXXXX.getKey(riderId), supplier, lockFail);}
tryLock 方法(重载):
    /*** 设置分布式锁*/public static <T> T tryLock(String key, Supplier<T> supplier, InvokeInter lockFail) {//这是 tryLock 的一个重载方法,它会在尝试获取锁时默认设置超时时间为 10 秒//通过 key、supplier 和 lockFail,它会调用 tryLock 的另一个重载版本,并使用 10 秒的默认超时时间return tryLock(key, supplier, lockFail, 10, TimeUnit.SECONDS);}
tryLock 方法(核心实现):
/*** 设置分布式锁-----------* key: 锁的标识符,通常是与某个资源(如骑手余额)相关的唯一标识。* supplier: 需要执行的业务逻辑。* lockFail: 获取锁失败时的回调操作。* amount 和 unit: 锁的持有时间(amount)和时间单位(unit),用于控制获取锁的最长等待时间。*/public static <T> T tryLock(String key, Supplier<T> supplier, InvokeInter lockFail, long amount, TimeUnit unit) {//通过 Redisson 客户端获取一个 RLock 对象,RLock 是 Redisson 提供的分布式锁实现。RLock lock = _this.redissonClient.getLock(key);try {// lock.isLocked():判断锁是否已经被其他线程持有。//lock.isHeldByCurrentThread():判断当前线程是否已经持有该锁。如果当前线程已经持有锁,它不需要重复获取。if (lock.isLocked() && !lock.isHeldByCurrentThread()) {if (lockFail == null) {// 如果 lockFail 为空,则抛出 BizException 异常,表示当前资源忙(例如余额操作正在进行)throw BizException.newInstance(ErrorCode.BUSY);}// 如果锁已经被其他线程持有且不是当前线程持有,且 lockFail 不为空,就执行 lockFail.invoke() 来处理锁获取失败的情况。lockFail.invoke();//lock.tryLock(amount, unit):尝试在指定的时间内(amount 秒)获取锁。如果成功,执行后续的业务操作。}  else if (lock.tryLock(amount, unit)) {try {// 如果锁获取成功,调用 supplier.get() 执行传入的业务逻辑。return supplier.get();} finally {//无论业务逻辑执行成功与否,都要确保释放锁,lock.unlock() 用于释放锁,防止死锁。lock.unlock();}} else {//如果在 tryLock 的超时时间内无法获得锁(即返回 false),就执行 lockFail.invoke(),或者抛出 BizException 异常表示资源繁忙if (lockFail == null) {throw BizException.newInstance(ErrorCode.BUSY);}lockFail.invoke();}//最终返回 null,因为所有锁的操作是围绕着锁获取和释放展开的,所有的业务执行通过 supplier.get() 来完成,返回值由 supplier 提供return null;} catch (InterruptedException e) {//如果线程在等待锁的过程中被中断,则抛出 BizException 异常,表示当前资源忙(此时无法完成操作)。throw BizException.newInstance(ErrorCode.BUSY);}}

这段代码主要实现了一个分布式锁的功能,通过 Redisson 提供的 RLock 来确保在分布式环境下对资源的访问是互斥的,避免多个线程或进程同时访问同一资源(如xxx余额)。

tryLock 方法用于尝试获取锁,并在成功获取锁时执行指定的业务操作(supplier.get())。如果获取锁失败,依据 lockFail 参数的值执行相应的失败处理操作。

锁的获取有超时机制,超过超时时间仍无法获取锁时会触发失败处理


http://www.ppmy.cn/server/145466.html

相关文章

Leetcode 每日一题 3.无重复字符的最长子串

目录 问题描述 输入输出格式 示例 滑动窗口算法步骤 通过图片 代码实现 复杂度分析 题目地址 注意事项 问题描述 给定一个字符串 s&#xff0c;我们需要找出其中不含有重复字符的最长子串的长度。子串是指字符串中连续的字符序列&#xff0c;而子序列则是字符序列&am…

FastDFS基础概述与系统架构详解

目录 一、FastDFS简介二、FastDFS系统架构1. 跟踪服务器&#xff08;Tracker Server&#xff09;2. 存储服务器&#xff08;Storage Server&#xff09;3. 客户端&#xff08;Client&#xff09; 三、FastDFS功能逻辑分析1. 文件上传&#xff08;Upload File&#xff09;原理2.…

如何安全高效地打开和管理动态链接库(DLL)?系统提示dll丢失问题的多种有效修复指南

动态链接库&#xff08;DLL&#xff09;文件是Windows操作系统中非常重要的一部分&#xff0c;它们包含了程序运行所需的代码和数据。当系统提示DLL文件丢失时&#xff0c;可能会导致应用程序无法正常运行。以下是一些安全高效地打开和管理DLL文件以及修复DLL丢失问题的方法&am…

常见面试题----深入源码理解MQ长轮询优化机制

引言 在分布式系统中&#xff0c;消息队列&#xff08;Message Queue, MQ&#xff09;扮演着至关重要的角色。MQ不仅实现了应用间的解耦&#xff0c;还提供了异步消息处理、流量削峰等功能。而在MQ的众多特性中&#xff0c;长轮询&#xff08;Long Polling&#xff09;机制因其…

[自动化]获取每次翻页后的页面 URL

from DrissionPage import ChromiumPage page ChromiumPage() page.get(热门项目 - Gitee.com) page.listen.start(gitee.com/explore) for i in range(5): page("relnext").click() res page.listen.wait() print(res.url) 这段代码使用了DrissionPage库中的Chromi…

C#基础46-50

46.数组x中有n个数&#xff0c;求出奇数的个数cn1和偶数的个数cn2以及数组x下标为偶数的元素值的算术平均值pj&#xff08;保留2位小数&#xff09;。结果cn1,cn2,pj输出到控制台。 47.求出10000以下符合条件的自然数。条件是&#xff1a;千位数字与百位数字之和等于十位数字与…

基于DVB-T的COFDM+16QAM+LDPC图传通信系统matlab仿真,包括载波同步,定时同步,信道估计

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 图传测试&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理…

蓝桥杯每日真题 - 第23天

题目&#xff1a;&#xff08;直线&#xff09; 题目描述&#xff08;12届 C&C B组C题&#xff09; 解题思路&#xff1a; 题目理解: 在平面直角坐标系中&#xff0c;从给定的点集中确定唯一的直线。 两点确定一条直线&#xff0c;判断两条直线是否相同&#xff0c;可通过…