锁工具类优化
This commit is contained in:
parent
a79d562895
commit
713a877df5
@ -1,9 +1,9 @@
|
||||
package com.red.circle.other.app.command;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.other.app.dto.clientobject.SpinsTaskRewardCO;
|
||||
import com.red.circle.other.app.dto.cmd.SpinsTaskReceiveRewardCmd;
|
||||
import com.red.circle.other.app.util.DistributedLockUtil;
|
||||
import com.red.circle.other.infra.database.rds.entity.task.SpinsTaskConfig;
|
||||
import com.red.circle.other.infra.database.rds.entity.task.SpinsUserTaskProgress;
|
||||
import com.red.circle.other.infra.database.rds.entity.task.SpinsUserTaskRecord;
|
||||
@ -14,7 +14,6 @@ import com.red.circle.other.inner.endpoint.activity.LotteryTicketClient;
|
||||
import com.red.circle.tool.core.date.TimestampUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -22,7 +21,6 @@ import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 领取任务奖励执行器
|
||||
@ -39,10 +37,9 @@ public class SpinsTaskReceiveRewardExe {
|
||||
private final SpinsUserTaskProgressDAO spinsUserTaskProgressDAO;
|
||||
private final SpinsUserTaskRecordDAO spinsUserTaskRecordDAO;
|
||||
private final LotteryTicketClient lotteryTicketClient;
|
||||
private final RedisService redisService;
|
||||
private final DistributedLockUtil distributedLockUtil;
|
||||
|
||||
private static final String LOCK_KEY_PREFIX = "spins:task:receive:lock:";
|
||||
private static final long LOCK_EXPIRE_TIME = 10; // 锁过期时间10秒
|
||||
private static final String LOCK_KEY_PREFIX = "spins:task:receive:lock";
|
||||
|
||||
/**
|
||||
* 执行领取任务奖励
|
||||
@ -56,35 +53,18 @@ public class SpinsTaskReceiveRewardExe {
|
||||
String taskCode = cmd.getTaskCode();
|
||||
|
||||
// 构建分布式锁key
|
||||
String lockKey = LOCK_KEY_PREFIX + userId + ":" + taskCode;
|
||||
String lockKey = DistributedLockUtil.buildLockKey(LOCK_KEY_PREFIX, userId, taskCode);
|
||||
|
||||
// 尝试获取锁
|
||||
boolean lockSuccess = redisService.setIfAbsent(lockKey, String.valueOf(System.currentTimeMillis()),
|
||||
LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
|
||||
|
||||
if (!lockSuccess) {
|
||||
log.warn("领取任务奖励请求过于频繁,请稍后再试, userId={}, taskCode={}", userId, taskCode);
|
||||
throw new RuntimeException("The request is too frequent. Please try again later");
|
||||
}
|
||||
|
||||
try {
|
||||
return executeReceiveReward(userId, taskCode);
|
||||
} finally {
|
||||
// 释放锁
|
||||
redisService.delete(lockKey);
|
||||
}
|
||||
// 使用分布式锁工具类执行业务逻辑
|
||||
return distributedLockUtil.executeWithLock(
|
||||
lockKey,
|
||||
() -> executeReceiveReward(userId, taskCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行领取任务奖励的核心逻辑
|
||||
*/
|
||||
private SpinsTaskRewardCO executeReceiveReward(Long userId, String taskCode) {
|
||||
try {
|
||||
Thread.sleep(1000L);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// 1. 查询任务配置
|
||||
List<SpinsTaskConfig> spinsTaskConfigs = spinsTaskConfigDAO.selectList(
|
||||
new LambdaQueryWrapper<SpinsTaskConfig>()
|
||||
@ -180,4 +160,4 @@ public class SpinsTaskReceiveRewardExe {
|
||||
private String getCurrentCycleKey() {
|
||||
return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,150 @@
|
||||
package com.red.circle.other.app.util;
|
||||
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 分布式锁工具类
|
||||
* 基于Redis实现的分布式锁,支持自动加锁/解锁
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-01-22
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DistributedLockUtil {
|
||||
|
||||
private final RedisService redisService;
|
||||
|
||||
/**
|
||||
* 默认锁过期时间(秒)
|
||||
*/
|
||||
private static final long DEFAULT_LOCK_EXPIRE_TIME = 10;
|
||||
|
||||
/**
|
||||
* 使用分布式锁执行业务逻辑(无返回值)
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param runnable 业务逻辑
|
||||
* @param errorMsg 获取锁失败时的错误信息
|
||||
*/
|
||||
public void executeWithLock(String lockKey, Runnable runnable, String errorMsg) {
|
||||
executeWithLock(lockKey, DEFAULT_LOCK_EXPIRE_TIME, runnable, errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用分布式锁执行业务逻辑(无返回值,自定义过期时间)
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param expireTime 锁过期时间(秒)
|
||||
* @param runnable 业务逻辑
|
||||
* @param errorMsg 获取锁失败时的错误信息
|
||||
*/
|
||||
public void executeWithLock(String lockKey, long expireTime, Runnable runnable, String errorMsg) {
|
||||
executeWithLock(lockKey, expireTime, () -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
}, errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用分布式锁执行业务逻辑(有返回值)
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param supplier 业务逻辑
|
||||
* @param errorMsg 获取锁失败时的错误信息
|
||||
* @param <T> 返回值类型
|
||||
* @return 业务逻辑执行结果
|
||||
*/
|
||||
public <T> T executeWithLock(String lockKey, Supplier<T> supplier, String errorMsg) {
|
||||
return executeWithLock(lockKey, DEFAULT_LOCK_EXPIRE_TIME, supplier, errorMsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用分布式锁执行业务逻辑(有返回值)
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param supplier 业务逻辑
|
||||
* @param <T> 返回值类型
|
||||
* @return 业务逻辑执行结果
|
||||
*/
|
||||
public <T> T executeWithLock(String lockKey, Supplier<T> supplier) {
|
||||
return executeWithLock(lockKey, DEFAULT_LOCK_EXPIRE_TIME, supplier, "The request is too frequent. Please try again later");
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用分布式锁执行业务逻辑(有返回值,自定义过期时间)
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param expireTime 锁过期时间(秒)
|
||||
* @param supplier 业务逻辑
|
||||
* @param errorMsg 获取锁失败时的错误信息
|
||||
* @param <T> 返回值类型
|
||||
* @return 业务逻辑执行结果
|
||||
*/
|
||||
public <T> T executeWithLock(String lockKey, long expireTime, Supplier<T> supplier, String errorMsg) {
|
||||
// 尝试获取锁
|
||||
boolean lockSuccess = tryLock(lockKey, expireTime);
|
||||
|
||||
if (!lockSuccess) {
|
||||
log.warn("获取分布式锁失败, lockKey={}", lockKey);
|
||||
throw new RuntimeException(errorMsg);
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug("获取分布式锁成功, lockKey={}", lockKey);
|
||||
return supplier.get();
|
||||
} finally {
|
||||
// 释放锁
|
||||
unlock(lockKey);
|
||||
log.debug("释放分布式锁, lockKey={}", lockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取锁
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param expireTime 锁过期时间(秒)
|
||||
* @return 是否获取成功
|
||||
*/
|
||||
public boolean tryLock(String lockKey, long expireTime) {
|
||||
return redisService.setIfAbsent(
|
||||
lockKey,
|
||||
String.valueOf(System.currentTimeMillis()),
|
||||
expireTime,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放锁
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
*/
|
||||
public void unlock(String lockKey) {
|
||||
redisService.delete(lockKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建锁的key
|
||||
*
|
||||
* @param prefix 前缀
|
||||
* @param keys key组成部分
|
||||
* @return 完整的锁key
|
||||
*/
|
||||
public static String buildLockKey(String prefix, Object... keys) {
|
||||
StringBuilder sb = new StringBuilder(prefix);
|
||||
for (Object key : keys) {
|
||||
sb.append(":").append(key);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user