边肖想和大家分享一下如何在Redis中实现支持几乎所有锁场景的分布式锁。相信大部分人还是不太了解。因此,我想分享这篇文章供你参考。希望你看完这篇文章后收获多多。让我们一起来看看。
依赖关系
groupIdorg.redisson/groupId
artifactidredison/artifactId
版本3 . 16 . 2/版本
/dependency copytoclipborderrorcopy
实战部分
/* *
*分布式锁自定义注释。
*/
@Target(ElementType。方法)
@Retention(保留策略。RUNTIME)
@已记录
public@interfaceLock{
/**
*锁定模式:如果没有设置自动模式,当只有一个参数时,使用MULTIPLE REENTRANT参数。
*/
lockmodelockmodel(). defaultLockModel。自动;
/**
*如果有多个键,如果没有设置,则使用互锁。
*
* @返回
*/
string[]key()默认为{ };
/**
* key的静态常数:当key的spel的值为LIST或array时,使用数字连接会让spel认为这个变量是字符串,只能产生一个锁,不能达到我们的目的。
*如果我们需要一个常数。此参数将在每个元素后拼接。
*
* @返回
*/
StringkeyConstant()(默认值“”;
/**
*锁定超时,默认为30000毫秒(可在配置文件中全局设置)。
*
* @返回
*/
longwatchDogTimeout()默认值30000;
/**
*等待锁定超时,默认值为10000毫秒-1表示一直等待(可以在配置文件中全局设置)。
*
* @返回
*/
long temperatimeout()default 10000;
}
1、引入redisson依赖
/** * Redisson常量类 */public class RedissonConst { /** * redisson锁默认前缀 */ public static final String REDISSON_LOCK = "redisson:lock:"; /** * spel表达式占位符 */ public static final String PLACE_HOLDER = "#";}4、枚举
/** * 锁的模式 */public enum LockModel { /** * 可重入锁 */ REENTRANT, /** * 公平锁 */ FAIR, /** * 联锁 */ MULTIPLE, /** * 红锁 */ RED_LOCK, /** * 读锁 */ READ, /** * 写锁 */ WRITE, /** * 自动模式,当参数只有一个使用 REENTRANT 参数多个 RED_LOCK */ AUTO}5、自定义异常
/** * 分布式锁异常 */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); }}6、AOP切面
/** * 分布式锁aop */@Slf4j@Aspectpublic 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("获取锁失败"); } /** * 获取加锁模式 * * @param lock * @param keys * @return */ 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; }}这里使用了策略模式来对不同的锁类型提供实现。
7、锁策略的实现
先定义锁策略的抽象基类(也可以用接口):
/**
* 锁策略抽象基类
*/
@Slf4j
abstract class LockStrategy {
@Autowired
private RedissonClient redissonClient;
/**
* 创建RLock
*
* @param keys
* @param parameterNames
* @param args
* @param keyConstant
* @return
*/
abstract RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient);
/**
* 获取RLock
*
* @param keys
* @param parameterNames
* @param args
* @param keyConstant
* @return
*/
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;
}
/**
* 通过spring Spel 获取参数
*
* @param key 定义的key值 以#开头 例如:#user
* @param parameterNames 形参
* @param args 形参值
* @param keyConstant key的常亮
* @return
*/
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);
}
}
8、使用方式
@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中如何实现支持几乎所有加锁场景的分布式锁”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/65134.html
