SpringBoot3+Redis+Lua脚本实现分布式锁
相关依赖包
3.0.2org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-data-redis
application.yml Redis配置
spring: data: redis: host: 192.168.5.133 port: 6379 password: database: 0 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-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 then if redis.call('SETNX', lockKey, requestId) then -- 设置锁的过期时间 redis.call('EXPIRE', lockKey, expireTime) return 1 end return 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 else return 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"; @Resource private StringRedisTemplate stringRedisTemplate; private static final DefaultRedisScript TRY_LOCK_SCRIPT = new DefaultRedisScript(); private static final DefaultRedisScript 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 seconds private static final int LOCK_TIMEOUT_SECONDS = 30000; private final RedisLockService lockService; @Autowired public LuaLockController(RedisLockService lockService) { this.lockService = lockService; } /** * 尝试获取锁并执行一些操作,然后释放锁。 * 通过尝试获取锁来确保操作的原子性,避免并发问题 */ @GetMapping("/performOperation") public String performOperation() { // 使用UUID作为请求ID String 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实现分布式锁
添加依赖包
org.redissonredisson-spring-boot-starter3.20.0
配置类
@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; @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer().setAddress("redis://" + host + ":" + port + ""); return Redisson.create(config); } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet(); return template; } }
实现类
@Resource private 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(); } } }
到此这篇关于SpringBoot3+Redis实现分布式锁的文章就介绍到这了,更多相关SpringBoot3 Redis分布式锁内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!