SpringBoot3+Redis实现分布式锁

SpringBoot3+Redis+Lua脚本实现分布式锁

相关依赖包

<spring-boot.version>3.0.2</spring-boot.version>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml Redis配置

spring:data:redis:host: 192.168.5.133port: 6379password:database: 0lettuce:pool:max-active: 8max-idle: 8min-idle: 0max-wait: "-1ms"

Lua脚本

获取锁脚本 tryLock.lua

-- KEYS[1] 是锁的键
-- ARGV[1] 是请求ID
-- ARGV[2] 是锁的过期时间(秒)
local lockKey = KEYS[1]
local requestId = ARGV[1]
local expireTime = tonumber(ARGV[2])-- 尝试获取锁
local lockValue = redis.call('GET', lockKey)-- 如果锁不存在,尝试设置锁
if not lockValue thenif redis.call('SETNX', lockKey, requestId) then-- 设置锁的过期时间redis.call('EXPIRE', lockKey, expireTime)return 1endreturn 0
elseif lockValue == requestId then-- 如果请求ID与当前锁持有者匹配,延长锁的过期时间redis.call('EXPIRE', lockKey, expireTime)return 1
else-- 锁被其他请求持有,无法获取锁return 0
end

释放锁脚本 releaseLock.lua

-- KEYS[1] 是锁的键
-- ARGV[1] 是请求ID
local lockKey = KEYS[1]
local requestId = ARGV[1]-- 获取锁的值
local lockValue = redis.call('GET', lockKey)-- 检查请求ID是否匹配锁的持有者
if lockValue == requestId then-- 删除锁redis.call('DEL', lockKey)return 1
elsereturn 0
end

Service层实现

package pub.qingyun.service;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;import java.util.List;/*** @author CQY* @version 1.0* @date 2024/7/10 10:29**/@Service
@Slf4j
public class RedisLockService {private static final String LOCK_KEY = "distributed-lock";@Resourceprivate StringRedisTemplate stringRedisTemplate;private static final DefaultRedisScript<Long> TRY_LOCK_SCRIPT = new DefaultRedisScript<>();private static final DefaultRedisScript<Long> RELEASE_LOCK_SCRIPT = new DefaultRedisScript<>();static {TRY_LOCK_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/tryLock.lua")));TRY_LOCK_SCRIPT.setResultType(Long.class);RELEASE_LOCK_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua")));RELEASE_LOCK_SCRIPT.setResultType(Long.class);}/*** 尝试获取分布式锁。** @param requestId  请求ID,用于唯一标识锁的持有者。* @param expireTime 锁的过期时间(秒)。* @return 如果成功获取锁返回true,否则返回false。*/public boolean tryLock(String requestId, int expireTime) {Object result = stringRedisTemplate.execute(TRY_LOCK_SCRIPT,List.of(LOCK_KEY),requestId,String.valueOf(expireTime));assert result != null;return Long.parseLong(result.toString()) == 1L;}/*** 释放分布式锁。** @param requestId 请求ID,必须与获取锁时使用的相同。* @return 如果锁成功释放返回true,否则返回false。*/public boolean releaseLock(String requestId) {Object result = stringRedisTemplate.execute(RELEASE_LOCK_SCRIPT,List.of(LOCK_KEY),requestId);assert result != null;return Long.parseLong(result.toString()) == 1L;}
}

Controller调用示例代码

package pub.qingyun.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import pub.qingyun.service.RedisLockService;import java.util.UUID;
import java.util.concurrent.TimeUnit;/*** @author CQY* @version 1.0* @date 2024/7/10 10:43**/
@Slf4j
@RestController
public class LuaLockController {// Lock timeout in secondsprivate static final int LOCK_TIMEOUT_SECONDS = 30000;private final RedisLockService lockService;@Autowiredpublic LuaLockController(RedisLockService lockService) {this.lockService = lockService;}/*** 尝试获取锁并执行一些操作,然后释放锁。* 通过尝试获取锁来确保操作的原子性,避免并发问题*/@GetMapping("/performOperation")public String performOperation() {// 使用UUID作为请求IDString requestId = UUID.randomUUID().toString();try {// 尝试获取锁boolean tryLock = lockService.tryLock(requestId, LOCK_TIMEOUT_SECONDS);log.info("获取锁[{}][{}]", requestId, tryLock);if (tryLock) {// 执行关键操作log.info("开始执行主任务[{}]...", requestId);TimeUnit.SECONDS.sleep(5); // 模拟耗时操作log.info("任务[{}]执行完成", requestId);return requestId + " completed successfully.";} else {log.info("无法获取锁,任务[{}]被拒绝", requestId);return "无法获取锁,任务[" + requestId + "]被拒绝";}} catch (InterruptedException e) {Thread.currentThread().interrupt();log.error("Interrupted while performing operation.", e);return "任务[" + requestId + "]执行失败";} finally {// 释放锁boolean releaseLock = lockService.releaseLock(requestId);log.info("释放锁[{}][{}]", requestId, releaseLock);}}
}

SpringBoot3+Redisson实现分布式锁

添加依赖包

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.20.0</version>
</dependency>

配置类

@Configuration
package pub.qingyun.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;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** @author CQY* @version 1.0* @date 2024/7/5 10:58**/
@Configuration
public class RedisConfig {@Value("${spring.data.redis.host}")private String host;@Value("${spring.data.redis.port}")private String port;@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://" + host + ":" + port + "");return Redisson.create(config);}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.afterPropertiesSet();return template;}
}

实现类

 	@Resourceprivate RedissonClient redissonClient;public void example() {RLock rlock = redissonClient.getLock("myLock");try {boolean locked = rlock.tryLock(0, 800, TimeUnit.MILLISECONDS);if (locked) {// TODO} else {log.warn("Thread[{}]Could not acquire lock.", Thread.currentThread().getName());}} catch (InterruptedException e) {Thread.currentThread().interrupt();log.error("Error occurred while trying to acquire lock.", e);} finally {if (rlock.isHeldByCurrentThread()) {rlock.unlock();}}}

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

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

相关文章

训练营第九天 | 151.翻转字符串里的单词、55.右旋转字符串、总结回顾

一、题目 151.翻转字符串里的单词 做题思路 代码细节 卡码网&#xff1a;55.右旋转字符串 做题思路 代码细节 二、总结回顾 &#xff08;一&#xff09;字符串总结 代码随想录 &#xff08;二&#xff09;双指针回顾 代码随想录

python多进程刻画进度

问题 python多进程启动4个任务&#xff0c;当前共20个任务&#xff0c;如何刻画当前进度&#xff1f; 解决方案 在 Python 中使用多进程处理多个任务并刻画当前进度&#xff0c;你可以使用 multiprocessing 模块和一个共享变量&#xff08;如 multiprocessing.Value 或 multi…

imx6ull/linux应用编程学习(17)利用mqtt上传开发板数据,和控制开发板led(基于正点)

1.关于如何创建自己的服务器&#xff0c;可看上篇文章 imx6ull/linux应用编程学习&#xff08;16&#xff09;emqx &#xff0c;mqtt创建连接mqtt.fx-CSDN博客 2.实现任务&#xff1a;&#xff08;正点原子教程源码改&#xff09; (1)用户可通过手机或电脑远程控制开发板上的…

小白的OS Copilot 产品测评

背景 通过群友介绍才知OS Copilot 。不想错过任何优秀的AI产品。随着互联网的发展和时代的进步&#xff0c;要紧跟时代&#xff0c;了解市面上的优秀的AI科技产品。 OS Copilot 产品体验评测 1&#xff09;您的角色是什么&#xff1f;开发、运维、学生&#xff1f;如果使用O…

Unity中短路法在背包系统的应用

一、计数法 int counter 0;foreach(GameObject slot in slotList) {if(slot.transform.childCount > 0){counter 1;} }return counter 21;计数法的复杂度为 O(n)&#xff0c;其中n 是插槽的数量。无论插槽是否已满&#xff0c;都会遍历所有插槽。 二、短路法 foreach (…

服务器怎么进PE系统?

服务器进PE是指将服务器的操作系统切换到预安装环境&#xff08;Pre-Installation Environment&#xff09;的状态。在PE环境下&#xff0c;可以进行一些系统管理和故障排除的操作。在进入PE&#xff08;Preinstall Environment&#xff09;之前&#xff0c;首先需要确保你的服…

类和对象——【const成员】【static成员】【友元】【内部类】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件iostream的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;Yan. yan.                        …

WPF 框架 Prism IActiveAware接口使用

目的 在Prism框架中&#xff0c;IActiveAware接口是一个重要的接口&#xff0c;它允许开发者实现组件或视图的激活和失活逻辑。这通常在需要响应用户界面的可见性或激活状态变化时非常有用&#xff0c;比如在选项卡式界面中切换标签页时。 如何使用IActiveAware IActiveAwar…

css文字自适应宽度动态出现省略号...

前言 在列表排行榜中通常会出现的一个需求&#xff1a;从左到右依次是名次、头像、昵称、徽标、分数。徽标可能会有多个或者没有徽标&#xff0c;徽标长度是动态的&#xff0c;昵称如果过长要随着有无徽标进行动态截断出现省略号。如下图布局所示&#xff08;花里胡哨的底色是…

SpringSecurity中文文档(Servlet OAuth 2.0 Login)

OAuth 2.0 Login OAuth 2.0 Login 功能允许应用程序让用户通过使用 OAuth 2.0 Provider (如 GitHub)或 OpenID Connect 1.0 Provider (如 Google)的现有帐户登录到应用程序。OAuth 2.0 Login 实现了两个用例: “ Login with Google”或“ Login with GitHub”。 OAuth 2.0 Lo…

若依vue集成electron实现打包exe应用程序

一、修改package.json文件,加入相关依赖和配置 {"name": "ruoyi","version": "3.8.6","description": "若依管理系统","author": "若依","license":

分层图最短路,CF 1725M - Moving Both Hands

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1725M - Moving Both Hands 二、解题报告 1、思路分析 题意看似很简单&#xff0c;就是问我们两个人分别处于1, p两个点上&#xff0c;二者同时出发&#xff0c;相遇时二者所用路径之和的最小值 不难想到求…

mysql默认开启索引下推,减少回表的数据

目的&#xff1a;在回表前就进行where多个条件的判断&#xff0c;从而回表到服务器层的数据量足够小 索引下推的工作原理 在没有索引下推优化的情况下&#xff0c;当 MySQL 使用索引进行查询时&#xff0c;它会从索引中获取符合条件的索引条目&#xff0c;然后回表&#xff0…

Ubuntu中如何设置IP地址

在 Ubuntu 中&#xff0c;可以通过几种方式设置 IP 地址&#xff1a;使用网络管理器图形界面、命令行工具&#xff08;如 nmcli 或 nmtui&#xff09;、或直接编辑网络配置文件。以下是这几种方法的详细步骤。 方法一&#xff1a;使用图形界面&#xff08;Network Manager&…

Git协作

文章目录 Git协作冲突冲突的发生情况解决冲突如何处理冲突 1 分支1.1 什么是Git分支1.2 创建分支 2 切换分支2.1 指向分支2.2 暂存分支切换分支与未提交更改的处理使用 Stash 临时保存更改Stash 的工作原理&#xff1a;场景设定使用 Git Stash 3 远程分支3.1 快进合并快进合并的…

13. 求余

问题描述 在 C/C/Java/Python 等语言中, 使用 % 表示求余, 请问 2021 % 20 的值是多少? 答案提交 这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。 运行限制 最大运行时间&#xff1…

Qt/QML学习-定位器

QML学习 定位器例程视频讲解代码 main.qml import QtQuick 2.15 import QtQuick.Window 2.15Window {width: 640height: 480visible: truetitle: qsTr("positioner")Rectangle {id: rectColumnwidth: parent.width / 2height: parent.height / 2border.width: 1Col…

Qt基础控件总结—多页面切换(QStackWidget类、QTabBar类和QTabWidget类)

QStackedWidget 类 QStackedWidget 类是在 QStackedLayout 之上构造的一个便利的部件,其使用方法与步骤和 QStackedLayout 是一样的。QStackedWidget 类的成员函数与 QStackedLayout 类也基本上是一致的,使用该类就和使用 QStackedLayout 一样。 使用该类可以参考QStackedL…

Perl 语言进阶学习

Perl 语言进阶学习 在掌握 Perl 的基础知识后&#xff0c;进一步学习 Perl 的高级特性和应用&#xff0c;将有助于提升编程效率和解决复杂问题的能力。本文将详细介绍 Perl 语言的高级功能、最佳实践以及实际应用案例。 目录 高级数据结构 多维数组复杂数据结构 引用与匿名数…

计算机如何学习

1. 不要只盯着计算机语言学习&#xff0c;你现在已经学习了C语言和Java&#xff0c;暑假又规划学习Python&#xff0c;最后你掌握的就是计算机语言包而已。 2. 建议你找一门想要深挖的语言&#xff0c;沿着这个方向继续往后学习知识就行。计算机语言是学不完的&#xff0c;而未…