SpringBoot3+Redis實現分布式鎖的配置方法
更新時間:2024年07月12日 10:33:35 作者:piaoyunlive
這篇文章主要介紹了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.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
endService層實現
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<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 seconds
private static final int LOCK_TIMEOUT_SECONDS = 30000;
private final RedisLockService lockService;
@Autowired
public LuaLockController(RedisLockService lockService) {
this.lockService = lockService;
}
/**
* 嘗試獲取鎖并執(zhí)行一些操作,然后釋放鎖。
* 通過嘗試獲取鎖來確保操作的原子性,避免并發(fā)問題
*/
@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) {
// 執(zhí)行關鍵操作
log.info("開始執(zhí)行主任務[{}]...", requestId);
TimeUnit.SECONDS.sleep(5); // 模擬耗時操作
log.info("任務[{}]執(zhí)行完成", 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 + "]執(zhí)行失敗";
} 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;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://" + host + ":" + port + "");
return Redisson.create(config);
}
@Bean
public 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;
}
}實現類
@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分布式鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Redis集群水平擴展、集群中添加以及刪除節(jié)點的操作
這篇文章主要介紹了Redis集群水平擴展、集群中添加以及刪除節(jié)點的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03

