1. 実施原則
-
レディソンをベースにしています。
-
アノテーションを実装したコードは次のとおりです。
@Target({ ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DistributedLock { long TEN_THOUSAND = 10000L; long EIGHT_THOUSAND = 8000L; /** * 锁的名称 */ String name() default ""; /** * 锁的过期时间,单位ms,默认8000 */ long expireMillis() default EIGHT_THOUSAND; /** * 获取锁的超时时间,单位ms,默认10000 */ long acquireTimeoutMillis() default TEN_THOUSAND; }
-
アスペクトを実装しました。メソッドが @DistributedLock アノテーションを使用する場合、メソッドを実行する前に分散ロックを取得します。コードは以下のように表示されます:
@Aspect @Component @Slf4j public class DistributedLockAspect { @Resource private RedissonClient redissonClient; @Pointcut("@annotation(com.xxx.xxx.xxx.xxx.DistributedLock)") public void myPointcut() { } @Around(value = "myPointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { DistributedLock distributedLock = getAnnotation(pjp); String lockName = resolveKey(distributedLock.name(), pjp); RLock lock = redissonClient.getLock(lockName); try { Stopwatch stopwatch = Stopwatch.createStarted(); boolean success = lock.tryLock(distributedLock.acquireTimeoutMillis(), distributedLock.expireMillis(), TimeUnit.MILLISECONDS); LOGGER.info("tryLock, result:{}, cost:{}", success, stopwatch.stop().elapsed(TimeUnit.MILLISECONDS)); if (success) { LOGGER.info("获取锁成功,lockName={}", lockName); } else { LOGGER.info("获取锁失败,lockName={}", lockName); } return pjp.proceed(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); LOGGER.info("释放锁成功,lockName={}", lockName); } } } private DistributedLock getAnnotation(ProceedingJoinPoint pjp) { MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); return methodSignature.getMethod().getAnnotation(DistributedLock.class); } private String resolveKey(String key, ProceedingJoinPoint pjp) { if (StringUtils.isBlank(key)) { return pjp.getSignature().toLongString(); } else { StandardEvaluationContext context = new StandardEvaluationContext(); Object[] args = pjp.getArgs(); MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); String[] params = methodSignature.getParameterNames(); for (int i = 0; i < params.length; i++) { context.setVariable(params[i], args[i]); } return new SpelExpressionParser().parseExpression(key).getValue(context, String.class); } } }
-
本質的に、これは
lock.tryLock(distributedLock.acquireTimeoutMillis(),distributedLock.expireMillis(),TimeUnit.MILLISECONDS);
次の方法で取得された分散ロックです。 -
distributedLock.acquireTimeoutMillis()
そうでないため0
、スレッドがロックを取得できない場合、スレッドは指定された時間待機し続け、その期間中はロックの取得を試行し続けます。通常はロックの取得に時間がかからないため、現在のデフォルトは次のとおりですdistributedLock.acquireTimeoutMillis()
。10s
ビジネス ロジックの実行に時間がかかるため、スレッドは通常は待機しません。 長時間待機します。10s
これは主に最終的な目的で使用されます。通常、ビジネスの実行にはそれほど時間がかからないため、
ロックの有効期限はdistributedLock.expireMillis()
現在デフォルトです。8s
このロジックのため、通常の状況では、スレッドは主に最終的な目的のために長時間ロックを保持しません8s
。取得する
ために使用します。SPEL
lockName
2. 使用方法
-
任意のレイヤーのメソッドにロックを追加します
@DistributedLock(name = "XXX")即可对"XXX"
。 -
例 1:
@DistributedLock(name = "'get'+#codes") public void get(String codes) { List<String> codeList = Arrays.stream(orgCodes.split(",")).collect(Collectors.toList()); ext(codeList); }
上記のメソッドの入力パラメータが
codes
の場合111,222,333,444
、SPEL
解析されたパラメータlockName
はget111,222,333,444
; -
例 2:
@DistributedLock(name = "'attendance_info'+#attendanceInfo.orderId+#attendanceInfo.date") public Integer insertDedup(ConstructionAttendanceInfo attendanceInfo, AtomicInteger dmlType) { // 此处省略 return 0; }
上記のメソッドの入力パラメータが
attendanceInfo
,orderId
isaaa
の場合date
、解析されたパラメータは;です。2023-08-01
SPEL
lockName
attendance_infoaaa2023-08-01
3. 注意事項
使用する場合は、異なるメソッドが同じ を生成することを避けるためにname
指定する必要があります。name = "'<方法名/唯一标识名>'+#<入参/入参的某个字段>"
lockName