一、目标:
? ? ? ? ? ? ? ? 1、简化手动开关锁的重复代码(专注业务本身)
? ? ? ? ? ? ? ? 2、集成不同分布式锁解决方案(锁不同使用方式不同)
? ? ? ? ? ? ? ? 3、规范锁的命名和异常信息内容(内容不规范,不易于理解和维护)
? ? ? ? ? ? ? ? 4、避免事务大于锁的优先级,造成幻读(事务和锁使用不熟)
二、基本需求:
? ? ? ? ? ? ? 锁的命名、异常信息、锁等待时间、快速失败、异常是否抛出
三、设计思想:
? ? ? ? ? ? ? ? ?通过springAOP和order注解在方法上切入解决事务和锁的优先级,提供统一开关锁接口loackManager集成不同分布式锁作为锁管理器,使用springEL表达式规范锁的命名和异常信息内容
四、常规分布式锁实现方案
1、内存容器,如concurrentHashMap
2、spring提供的
jdbc-lock-registry、redis-lock-registry、zk-lock-registry
五、代码实现:(目前暂实现concurrentHashMap和redis,如有需要自己拓展)
/**
* @description: DistributionLock <br>
* 建议:'领域对象:对象行为:'+#业务参数;层级前加:(redis结构清晰),参数前#(spel语法)
* @SyncLock(key = "'branchCompany:cece:' + #dto.diposeOrgId, lockManager =
* "redis")
* @date: 2023/01/15 15:26 <br>
* @author: arthur<br>
* @version: 1.0 <br>
*/
@Target({METHOD})
@Retention(RUNTIME)
public @interface SyncLock {
/**
* 锁管理器
*
* @return 锁管理器
*/
String lockManager() default "jvm";
/**
* SpEL表达式支持
*
* @return SpEL表达式
*/
String key();
/**
* SpEL表达式支持
*
* @return SpEL表达式
*/
String errorMsg() default "";
/**
* 快速失败
*
* @return 快速失败
*/
boolean fastFail() default false;
/**
* 锁等待时间(单位:秒,默认10S)
*
* @return 锁等待时间
*/
long waitTime() default 10;
/**
* 异常是否抛出
*
* @return 默认true
*/
boolean throwError() default true;
}
/**
* @description: LockManager <br>
* @date: 2023/01/15 15:26 <br>
* @author: arthur<br>
* @version: 1.0 <br>
*/
public interface LockManager {
Lock getLock(String lockName);
}
/**
* @description: JvmLockManager <br>
* @date: 2023/01/15 15:26 <br>
* @author: arthur<br>
* @version: 1.0 <br>
*/
public class JvmLockManager implements LockManager {
private final Map<String, Lock> cache = new ConcurrentHashMap<>();
@Override
public Lock getLock(String lockName) {
return cache.computeIfAbsent(lockName, k -> new ReentrantLock());
}
}
/**
* @description: RedissionLockManager <br>
* @date: 2023/01/15 15:26 <br>
* @author: arthur<br>
* @version: 1.0 <br>
*/
@Component
@RequiredArgsConstructor
public class RedisLockManager implements LockManager {
private final RedissonClient redissonClient;
@Override
public Lock getLock(String lockName) {
return redissonClient.getLock(lockName);
}
}
/**
* @description: DistributionLockAspect <br>
* @date: 2023/01/15 15:26 <br>
* @author: arthur<br>
* @version: 1.0 <br>
*/
@Slf4j
@Aspect
@RequiredArgsConstructor
@Order(1)
public class SyncLockAspect implements InitializingBean, ApplicationContextAware {
private final ExpressionParser parser = new SpelExpressionParser();
private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
private ApplicationContext context;
private final Map<String, LockManager> lockManagerMap = new HashMap<>();
@Pointcut(value = "@annotation(com.runlion.hshb.common.lock.annotation.SyncLock)")
public void SyncLock() {
}
@Around("SyncLock() && @annotation(syncLock)")
public Object inject(ProceedingJoinPoint pjp, SyncLock syncLock) throws Throwable {
Signature s = pjp.getSignature();
MethodSignature ms = (MethodSignature) s;
Method method = ms.getMethod();
String lockKey = parseSpel(method, pjp.getArgs(), syncLock.key());
LockManager lockManager = lockManagerMap.get(syncLock.lockManager());
if (null == lockManager) {
log.warn("未获取到同步锁管理器:{}", syncLock.lockManager());
return pjp.proceed();
}
Lock lock = lockManager.getLock(lockKey);
boolean getLock;
//是否快速失败
if (syncLock.fastFail()) {
getLock = lock.tryLock();
} else {
getLock = lock.tryLock(syncLock.waitTime(), TimeUnit.SECONDS);
}
//判断是否获取到锁
if (getLock) {
try {
return pjp.proceed();
} finally {
lock.unlock();
}
} else if (syncLock.throwError()) {
if (StringUtils.isNotBlank(syncLock.errorMsg())) {
throw new IllegalArgumentException(parseSpel(method, pjp.getArgs(), syncLock.errorMsg()));
} else {
throw new IllegalArgumentException("无法获取分布式锁[" + lockKey + "]");
}
} else {
log.info("无法获取lockKey:: {}", lockKey);
return null;
}
}
private String parseSpel(Method method, Object[] arguments, String spelExpression) {
String[] params = discoverer.getParameterNames(method);
assert params != null;
EvaluationContext context = new StandardEvaluationContext();
for (int len = 0; len < params.length; len++) {
context.setVariable(params[len], arguments[len]);
}
try {
Expression expression = parser.parseExpression(spelExpression);
return expression.getValue(context, String.class);
} catch (Exception e) {
return spelExpression;
}
}
@Override
public void afterPropertiesSet() {
Map<String, LockManager> managerMap = this.context.getBeansOfType(LockManager.class);
for (Map.Entry<String, LockManager> entry : managerMap.entrySet()) {
this.lockManagerMap.put(StringUtils.removeEnd(entry.getKey(), "LockManager"), entry.getValue());
}
log.info("已安装同步锁管理器:{}", this.lockManagerMap.keySet());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}
/**
* @description: DistributionLockAutoConfiguration <br>
* @date: 2023/01/15 15:26 <br>
* @author: arthur<br>
* @version: 1.0 <br>
*/
@Configuration
public class SyncLockAutoConfiguration {
@Bean
public SyncLockAspect syncLockAspect() {
return new SyncLockAspect();
}
@Bean
public JvmLockManager jvmLockManager() {
return new JvmLockManager();
}
@Configuration
@ConditionalOnProperty(prefix = "lock", value = "distributed", havingValue = "true")
@Import({CacheConfiguration.class})
@AutoConfigureAfter({CacheConfiguration.class})
public static class DistributionLockConfiguration {
@Bean
public RedisLockManager redisLockManager(RedissonClient redissonClient) {
return new RedisLockManager(redissonClient);
}
}
}