註解形式實現,Redis分散式鎖
阿新 • • 發佈:2018-11-02
Redis工具類參考我的博文:https://blog.csdn.net/weixin_38399962/article/details/82753763
一個註解就可以實現分散式鎖?這麼神奇麼?
首先定義註解:
/** * Description:分散式Redis鎖 * User: zhouzhou * Date: 2018-09-25 * Time: 10:55 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface DLock { String lockName() default ""; // 鎖名 int retryTimes() default 0; // 重試次數 long retryWait() default 0; // 重試等待時間,單位 : ms }
再定義切面:
* 先獲取鎖, 獲取不到則繼續等待(指定時間), 失敗次數(指定)次後跳出, 消費降級(丟擲,系統繁忙稍後再試)
* 如果沒有重試次數,方法返回null 記得捕獲NP
* 當重試次數有, 但是重試間隔時間沒寫, 預設200ms 間隔
/** * Description: 分散式鎖 * <p> * 先獲取鎖, 獲取不到則繼續等待(指定時間), 失敗次數(指定)次後跳出, 消費降級(丟擲,系統繁忙稍後再試) * 如果沒有重試次數,方法返回null 記得捕獲NP * 當重試次數有, 但是重試間隔時間沒寫, 預設200ms 間隔 * </p> * User: zhouzhou * Date: 2018-09-25 * Time: 10:58 * version: 1.0 */ @Aspect @Component public class DLockAspect { private static final Logger log = LoggerFactory.getLogger(DLockAspect.class); private static final String LOCK_NAME = "lockName"; private static final String RETRY_TIMES = "retryTimes"; private static final String RETRY_WAIT = "retryWait"; @Autowired private CommonRedisHelper redisHelper; @Pointcut("@annotation(com.shuige.components.cache.annotation.DLock)") public void dLockAspect() { } @Around("dLockAspect()") public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable { System.out.println("分散式鎖測試"); //獲取註解中的引數 Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding); String lockName = (String) annotationArgs.get(LOCK_NAME); Assert.notNull(lockName, "分散式,鎖名不能為空"); int retryTimes = (int) annotationArgs.get(RETRY_TIMES); long retryWait = (long) annotationArgs.get(RETRY_WAIT); // 獲取鎖 boolean lock = redisHelper.lock(lockName); if (lock) { // 執行主邏輯 return execut(proceeding, lockName); } else { // 如果重試次數為零, 則不重試 if (retryTimes <= 0) { log.info(String.format("{%s}已經被鎖, 不重試", lockName)); throw new RuntimeException(String.format("{%s}已經被鎖, 不重試", lockName)); } if (retryWait == 0) { retryWait = 200; } // 設定失敗次數計數器, 當到達指定次數時, 返回失敗 int failCount = 1; while (failCount <= retryTimes) { // 等待指定時間ms try { Thread.sleep(retryWait); } catch (InterruptedException e) { e.printStackTrace(); } if (redisHelper.lock(lockName)) { // 執行主邏輯 return execut(proceeding, lockName); } else { log.info(String.format("{%s}已經被鎖, 正在重試[ %s/%s ],重試間隔{%s}毫秒", lockName, failCount, retryTimes, retryWait)); failCount++; } } throw new RuntimeException("系統繁忙, 請稍等再試"); } } private Object execut(ProceedingJoinPoint proceeding, String lockName) throws Throwable { try { // 執行主邏輯 Object proceed = proceeding.proceed(); return proceed; } catch (Throwable throwable) { log.error(String.format("執行分散式鎖發生異常鎖名:{%s},異常名稱:{%s}", lockName, throwable.getMessage())); throw throwable; } finally { redisHelper.delete(lockName); } } /** * 獲取鎖引數 * * @param proceeding * @return */ private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) { Class target = proceeding.getTarget().getClass(); Method[] methods = target.getMethods(); String methodName = proceeding.getSignature().getName(); for (Method method : methods) { if (method.getName().equals(methodName)) { Map<String, Object> result = new HashMap<String, Object>(); DLock redisLock = method.getAnnotation(DLock.class); String firstArg = getFirstArg(proceeding); result.put(LOCK_NAME, redisLock.lockName().concat("_").concat(firstArg)); result.put(RETRY_TIMES, redisLock.retryTimes()); result.put(RETRY_WAIT, redisLock.retryWait()); return result; } } return null; } /** * 獲取第一個String型別的引數為鎖的業務引數 * * @param proceeding * @return */ public String getFirstArg(ProceedingJoinPoint proceeding) { Object[] args = proceeding.getArgs(); if (args != null && args.length > 0) { for (Object object : args) { String type = object.getClass().getName(); if ("java.lang.String".equals(type)) { return (String) object; } } } return null; } }
使用場景:
在方法上定義註解即可, redis的key就是 lockName_cityCode, 重試次數5次, 重試間隔300ms