您好,登錄后才能下訂單哦!
小編給大家分享一下Redis中如何實(shí)現(xiàn)支持幾乎所有加鎖場(chǎng)景的分布式鎖,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
<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 { /** * 鎖的模式:如果不設(shè)置自動(dòng)模式,當(dāng)參數(shù)只有一個(gè).使用 REENTRANT 參數(shù)多個(gè) MULTIPLE */ LockModel lockModel() default LockModel.AUTO; /** * 如果keys有多個(gè),如果不設(shè)置,則使用 聯(lián)鎖 * * @return */ String[] keys() default {}; /** * key的靜態(tài)常量:當(dāng)key的spel的值是LIST、數(shù)組時(shí)使用+號(hào)連接將會(huì)被spel認(rèn)為這個(gè)變量是個(gè)字符串,只能產(chǎn)生一把鎖,達(dá)不到我們的目的, * 而我們?nèi)绻中枰粋€(gè)常量的話。這個(gè)參數(shù)將會(huì)在拼接在每個(gè)元素的后面 * * @return */ String keyConstant() default ""; /** * 鎖超時(shí)時(shí)間,默認(rèn)30000毫秒(可在配置文件全局設(shè)置) * * @return */ long watchDogTimeout() default 30000; /** * 等待加鎖超時(shí)時(shí)間,默認(rèn)10000毫秒 -1 則表示一直等待(可在配置文件全局設(shè)置) * * @return */ long attemptTimeout() default 10000; }
/** * Redisson常量類(lèi) */ public class RedissonConst { /** * redisson鎖默認(rèn)前綴 */ public static final String REDISSON_LOCK = "redisson:lock:"; /** * spel表達(dá)式占位符 */ public static final String PLACE_HOLDER = "#"; }
/** * 鎖的模式 */ public enum LockModel { /** * 可重入鎖 */ REENTRANT, /** * 公平鎖 */ FAIR, /** * 聯(lián)鎖 */ MULTIPLE, /** * 紅鎖 */ RED_LOCK, /** * 讀鎖 */ READ, /** * 寫(xiě)鎖 */ WRITE, /** * 自動(dòng)模式,當(dāng)參數(shù)只有一個(gè)使用 REENTRANT 參數(shù)多個(gè) RED_LOCK */ 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); } }
/** * 分布式鎖aop */ @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數(shù)組 String[] keys = lock.keys(); if (ArrayUtil.isEmpty(keys)) { throw new ReddissonException("redisson lock keys不能為空"); } // 獲取方法的參數(shù)名 String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod()); Object[] args = proceedingJoinPoint.getArgs(); // 等待鎖的超時(shí)時(shí)間 long attemptTimeout = lock.attemptTimeout(); if (attemptTimeout == 0) { attemptTimeout = redissonProperties.getAttemptTimeout(); } // 鎖超時(shí)時(shí)間 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("參數(shù)有多個(gè),鎖模式為->" + lockModel.name() + ",無(wú)法匹配加鎖"); } log.info("鎖模式->{},等待鎖定時(shí)間->{}毫秒,鎖定最長(zhǎng)時(shí)間->{}毫秒", lockModel.name(), attemptTimeout, lockWatchdogTimeout); boolean res = false; // 策略模式獲取redisson鎖對(duì)象 RLock rLock = lockStrategyFactory.createLock(lockModel, keys, parameterNames, args, lock.keyConstant(), redissonClient); //執(zhí)行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(); // 自動(dòng)模式:優(yōu)先匹配全局配置,再判斷用紅鎖還是可重入鎖 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; } }
這里使用了策略模式
來(lái)對(duì)不同的鎖類(lèi)型提供實(shí)現(xiàn)。
先定義鎖策略的抽象基類(lèi)(也可以用接口):
/** * 鎖策略抽象基類(lèi) */ @Slf4j abstract class LockStrategy { @Autowired private RedissonClient redissonClient; /** * 創(chuàng)建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; } /** * 通過(guò)spring Spel 獲取參數(shù) * * @param key 定義的key值 以#開(kāi)頭 例如:#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("沒(méi)有使用spel表達(dá)式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表達(dá)式key={},value={}", key, keys); return keys; } }
再提供各種鎖模式的具體實(shí)現(xiàn):
可重入鎖:
/** * 可重入鎖策略 */ 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表達(dá)式是數(shù)組或者集合 則使用紅鎖 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)); } }
聯(lián)鎖
/** * 聯(lián)鎖策略 */ 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(); } }
寫(xiě)鎖
/** * 寫(xiě)鎖策略 */ 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(); } }
最后提供一個(gè)策略工廠初始化鎖策略:
/** * 鎖的策略工廠 */ @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("分頁(yè)列表") @GetMapping public ApiPageResult list(VendorProjectItemQuery query, Pagination pagination) { return ApiPageResult.success(pagination, vendorProjectItemService.list(query, pagination), vendorProjectItemService.count(query)); }
以上是“Redis中如何實(shí)現(xiàn)支持幾乎所有加鎖場(chǎng)景的分布式鎖”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。