欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > SpringBoot3+Redis实现分布式锁

SpringBoot3+Redis实现分布式锁

2024/10/24 9:29:15 来源:https://blog.csdn.net/piaoyunlive/article/details/140318761  浏览:    关键词: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();}}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com