小编给大家分享一下Redis中如何实现支持几乎所有加锁场景的分布式锁,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!实战部分1、引
小编给大家分享一下Redis中如何实现支持几乎所有加锁场景的分布式锁,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.2</version>
</dependency>Copy to clipboardErrorCopied
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock {
LockModel lockModel() default LockModel.AUTO;
String[] keys() default {};
String keyConstant() default "";
long watchDogTimeout() default 30000;
long attemptTimeout() default 10000;
}
public class RedissonConst {
public static final String REDISSON_LOCK = "redisson:lock:";
public static final String PLACE_HOLDER = "#";
}
public enum LockModel {
REENTRANT,
FaiR,
MULTIPLE,
RED_LOCK,
READ,
WRITE,
AUTO
}
public class ReddissonException extends RuntimeException {
public ReddissonException() {
}
public ReddissonException(String message) {
super(message);
}
public ReddissonException(String message, Throwable cause) {
super(message, cause);
}
public ReddissonException(Throwable cause) {
super(cause);
}
public ReddissonException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@Slf4j
@Aspect
public class Lockaop {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private LockStrategyFactory lockStrategyFactory;
@Around("@annotation(lock)")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, Lock lock) throws Throwable {
// 需要加锁的key数组
String[] keys = lock.keys();
if (ArrayUtil.isEmpty(keys)) {
throw new ReddissonException("redisson lock keys不能为空");
}
// 获取方法的参数名
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod());
Object[] args = proceedingJoinPoint.getArgs();
// 等待锁的超时时间
long attemptTimeout = lock.attemptTimeout();
if (attemptTimeout == 0) {
attemptTimeout = redissonProperties.getAttemptTimeout();
}
// 锁超时时间
long lockWatchdogTimeout = lock.watchdogTimeout();
if (lockWatchdogTimeout == 0) {
lockWatchdogTimeout = redissonProperties.getLockWatchdogTimeout();
}
// 加锁模式
LockModel lockModel = getLockModel(lock, keys);
if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.RED_LOCK) && keys.length > 1) {
throw new ReddissonException("参数有多个,锁模式为->" + lockModel.name() + ",无法匹配加锁");
}
log.info("锁模式->{},等待锁定时间->{}毫秒,锁定最长时间->{}毫秒", lockModel.name(), attemptTimeout, lockWatchdogTimeout);
boolean res = false;
// 策略模式获取redisson锁对象
RLock rLock = lockStrategyFactory.createLock(lockModel, keys, parameterNames, args, lock.keyConstant(), redissonClient);
//执行aop
if (rLock != null) {
try {
if (attemptTimeout == -1) {
res = true;
//一直等待加锁
rLock.lock(lockWatchdogTimeout, TimeUnit.MILLISECONDS);
} else {
res = rLock.tryLock(attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS);
}
if (res) {
return proceedingJoinPoint.proceed();
} else {
throw new ReddissonException("获取锁失败");
}
} finally {
if (res) {
rLock.unlock();
}
}
}
throw new ReddissonException("获取锁失败");
}
private LockModel getLockModel(Lock lock, String[] keys) {
LockModel lockModel = lock.lockModel();
// 自动模式:优先匹配全局配置,再判断用红锁还是可重入锁
if (lockModel.equals(LockModel.AUTO)) {
LockModel globalLockModel = redissonProperties.getLockModel();
if (globalLockModel != null) {
lockModel = globalLockModel;
} else if (keys.length > 1) {
lockModel = LockModel.RED_LOCK;
} else {
lockModel = LockModel.REENTRANT;
}
}
return lockModel;
}
}
这里使用了策略模式
来对不同的锁类型提供实现。
先定义锁策略的抽象基类(也可以用接口):
@Slf4j
abstract class LockStrategy {
@Autowired
private RedissonClient redissonClient;
abstract RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient);
public RLock[] getRLocks(String[] keys, String[] parameterNames, Object[] args, String keyConstant) {
List<RLock> rLocks = new ArrayList<>();
for (String key : keys) {
List<String> valueBySpel = getValueBySpel(key, parameterNames, args, keyConstant);
for (String s : valueBySpel) {
rLocks.add(redissonClient.getLock(s));
}
}
RLock[] locks = new RLock[rLocks.size()];
int index = 0;
for (RLock r : rLocks) {
locks[index++] = r;
}
return locks;
}
List<String> getValueBySpel(String key, String[] parameterNames, Object[] args, String keyConstant) {
List<String> keys = new ArrayList<>();
if (!key.contains(PLACE_HOLDER)) {
String s = REDISSON_LOCK + key + keyConstant;
log.info("没有使用spel表达式value->{}", s);
keys.add(s);
return keys;
}
// spel解析器
ExpressionParser parser = new SpelExpressionParser();
// spel上下文
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
Expression expression = parser.parseExpression(key);
Object value = expression.getValue(context);
if (value != null) {
if (value instanceof List) {
List valueList = (List) value;
for (Object o : valueList) {
keys.add(REDISSON_LOCK + o.toString() + keyConstant);
}
} else if (value.getClass().isArray()) {
Object[] objects = (Object[]) value;
for (Object o : objects) {
keys.add(REDISSON_LOCK + o.toString() + keyConstant);
}
} else {
keys.add(REDISSON_LOCK + value.toString() + keyConstant);
}
}
log.info("spel表达式key={},value={}", key, keys);
return keys;
}
}
再提供各种锁模式的具体实现:
可重入锁:
public class ReentrantLockStrategy extends LockStrategy {
@Override
public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
List<String> valueBySpel = getValueBySpel(keys[0], parameterNames, args, keyConstant);
//如果spel表达式是数组或者集合 则使用红锁
if (valueBySpel.size() == 1) {
return redissonClient.getLock(valueBySpel.get(0));
} else {
RLock[] locks = new RLock[valueBySpel.size()];
int index = 0;
for (String s : valueBySpel) {
locks[index++] = redissonClient.getLock(s);
}
return new RedissonRedLock(locks);
}
}
}
公平锁:
public class FairLockStrategy extends LockStrategy {
@Override
public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
return redissonClient.getFairLock(getValueBySpel(keys[0], parameterNames, args, keyConstant).get(0));
}
}
联锁
public class MultipleLockStrategy extends LockStrategy {
@Override
public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
RLock[] locks = getRLocks(keys, parameterNames, args, keyConstant);
return new RedissonMultiLock(locks);
}
}
红锁
public class RedLockStrategy extends LockStrategy {
@Override
public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
RLock[] locks = getRLocks(keys, parameterNames, args, keyConstant);
return new RedissonRedLock(locks);
}
}
读锁
public class ReadLockStrategy extends LockStrategy {
@Override
public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
RReadWriteLock rwLock = redissonClient.getReadWriteLock(getValueBySpel(keys[0], parameterNames, args, keyConstant).get(0));
return rwLock.readLock();
}
}
写锁
public class WriteLockStrategy extends LockStrategy {
@Override
public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
RReadWriteLock rwLock = redissonClient.getReadWriteLock(getValueBySpel(keys[0], parameterNames, args, keyConstant).get(0));
return rwLock.writeLock();
}
}
最后提供一个策略工厂初始化锁策略:
@Service
public class LockStrategyFactory {
private LockStrategyFactory() {
}
private static final Map<LockModel, LockStrategy> STRATEGIES = new HashMap<>(6);
static {
STRATEGIES.put(LockModel.FAIR, new FairLockStrategy());
STRATEGIES.put(LockModel.REENTRANT, new ReentrantLockStrategy());
STRATEGIES.put(LockModel.RED_LOCK, new RedLockStrategy());
STRATEGIES.put(LockModel.READ, new ReadLockStrategy());
STRATEGIES.put(LockModel.WRITE, new WriteLockStrategy());
STRATEGIES.put(LockModel.MULTIPLE, new MultipleLockStrategy());
}
public RLock createLock(LockModel lockModel, String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) {
return STRATEGIES.get(lockModel).createLock(keys, parameterNames, args, keyConstant, redissonClient);
}
}
@Lock(keys = "#query.channel") // 支持spel
@apiOperation("分页列表")
@GetMapping
public ApiPageResult list(VendorProjectIteMQuery query, Pagination pagination) {
return ApiPageResult.success(pagination, vendorProjectItemService.list(query, pagination), vendorProjectItemService.count(query));
}
以上是“Redis中如何实现支持几乎所有加锁场景的分布式锁”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网数据库频道!
--结束END--
本文标题: Redis中如何实现支持几乎所有加锁场景的分布式锁
本文链接: https://www.lsjlt.com/news/62635.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0