打印增加等待锁,防止重复打印
This commit is contained in:
@@ -6,8 +6,10 @@ import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
@@ -24,29 +26,60 @@ public class FunUtil {
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
public static int retryCount = 5;
|
||||
|
||||
/**
|
||||
* 执行任务并保证锁唯一
|
||||
* @param supplier 业务逻辑
|
||||
* @param lockKey Redis锁的Key
|
||||
* @return 业务逻辑返回值
|
||||
*/
|
||||
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
|
||||
try{
|
||||
// 设置分布式锁
|
||||
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.MILLISECONDS));
|
||||
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
|
||||
try {
|
||||
// 尝试获取锁,超时时间 5 秒,防止死锁
|
||||
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
||||
int count = 0;
|
||||
// 初始等待 10ms
|
||||
int retryDelay = 10;
|
||||
|
||||
while (!lock) {
|
||||
if (count++ > 100) {
|
||||
throw new ApiNotPrintException("系统繁忙, 稍后再试");
|
||||
// 最多重试 10 次,大约 10 秒
|
||||
if (count++ > 50) {
|
||||
throw new RuntimeException("系统繁忙, 稍后再试");
|
||||
}
|
||||
Thread.sleep(20);
|
||||
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.MILLISECONDS));
|
||||
Thread.sleep(retryDelay);
|
||||
// 指数退避,最大等待 200ms
|
||||
retryDelay = Math.min(retryDelay * 2, 200);
|
||||
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
// 执行任务
|
||||
return supplier.get();
|
||||
} catch (RuntimeException e){
|
||||
log.info("执行出错:{}", e.getMessage());
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally{
|
||||
redisTemplate.delete(lockKey);
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("线程被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("执行出错:{}", e.getMessage(), e);
|
||||
throw e;
|
||||
} finally {
|
||||
// 释放锁(使用 Lua 脚本确保原子性)
|
||||
unlock(lockKey, lockValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 Lua 脚本确保释放锁的原子性
|
||||
* @param lockKey 锁的 Key
|
||||
* @param lockValue 当前线程的锁值
|
||||
*/
|
||||
private void unlock(String lockKey, String lockValue) {
|
||||
String luaScript =
|
||||
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
|
||||
"return redis.call('del', KEYS[1]) " +
|
||||
"else return 0 end";
|
||||
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
|
||||
Collections.singletonList(lockKey), lockValue);
|
||||
}
|
||||
|
||||
public static <T, R> R runFunAndRetry(
|
||||
Supplier<R> function,
|
||||
Function<R, Boolean> check, Consumer<R> errFun) {
|
||||
|
||||
Reference in New Issue
Block a user