组件讲解-分布式锁原理的详细剖析-上
本文将详细介绍分布式锁的设计架构,其中应用了大量的设计模式,和Spring的自动装配,相信小伙伴看完后会对架构组件的设计有更深入的理解
依赖
<dependency>
<groupId>com.example</groupId>
<artifactId>damai-service-lock-framework</artifactId>
<version>${revision}</version>
</dependency>ServiceLockAutoConfiguration
public class ServiceLockAutoConfiguration {
/**
* 分布式锁的key解析处理器
* */
@Bean(LockInfoType.SERVICE_LOCK)
public LockInfoHandle serviceLockInfoHandle(){
return new ServiceLockInfoHandle();
}
/**
* 锁管理
* */
@Bean
public ManageLocker manageLocker(RedissonClient redissonClient){
return new ManageLocker(redissonClient);
}
/**
* 锁工厂
* */
@Bean
public ServiceLockFactory serviceLockFactory(ManageLocker manageLocker){
return new ServiceLockFactory(manageLocker);
}
/**
* 分布式锁切面
* */
@Bean
public ServiceLockAspect serviceLockAspect(LockInfoHandleFactory lockInfoHandleFactory,ServiceLockFactory serviceLockFactory){
return new ServiceLockAspect(lockInfoHandleFactory,serviceLockFactory);
}
/**
* 分布式锁工具
* */
@Bean
public ServiceLockTool serviceLockUtil(LockInfoHandleFactory lockInfoHandleFactory,ServiceLockFactory serviceLockFactory){
return new ServiceLockTool(lockInfoHandleFactory,serviceLockFactory);
}
}ServiceLockAutoConfiguration是自动装配类,加载了 分布式锁的key解析处理器、分布式锁工厂、分布式锁切面、分布式锁工具的对象
ManageLocker
锁管理缓存,这里将重入锁、公平锁、写锁、读锁的实例给缓存起来
public class ManageLocker {
private final Map<LockType, ServiceLocker> cacheLocker = new HashMap<>();
public ManageLocker(RedissonClient redissonClient){
cacheLocker.put(Reentrant,new RedissonReentrantLocker(redissonClient));
cacheLocker.put(Fair,new RedissonFairLocker(redissonClient));
cacheLocker.put(Write,new RedissonWriteLocker(redissonClient));
cacheLocker.put(Read,new RedissonReadLocker(redissonClient));
}
public ServiceLocker getReentrantLocker(){
return cacheLocker.get(Reentrant);
}
public ServiceLocker getFairLocker(){
return cacheLocker.get(Fair);
}
public ServiceLocker getWriteLocker(){
return cacheLocker.get(Write);
}
public ServiceLocker getReadLocker(){
return cacheLocker.get(Read);
}
}ServiceLockAspect
下面我们来分析具体的切面执行逻辑
@Around("@annotation(servicelock)")
public Object around(ProceedingJoinPoint joinPoint, ServiceLock servicelock) throws Throwable {
//获取锁的名字解析处理器
LockInfoHandle lockInfoHandle = lockInfoHandleFactory.getLockInfoHandle(LockInfoType.SERVICE_LOCK);
//拼接锁的名字 LOCK:${name}:${key}
String lockName = lockInfoHandle.getLockName(joinPoint, servicelock.name(),servicelock.keys());
//锁的类型,默认 可重入锁
LockType lockType = servicelock.lockType();
//尝试加锁失败最多等待时间,默认10s
long waitTime = servicelock.waitTime();
//时间单位,默认秒
TimeUnit timeUnit = servicelock.timeUnit();
//获得具体的锁类型
ServiceLocker lock = serviceLockFactory.getLock(lockType);
//进行加锁
boolean result = lock.tryLock(lockName, timeUnit, waitTime);
//如果加锁成功
if (result) {
try {
//执行业务逻辑
return joinPoint.proceed();
}finally{
//解锁
lock.unlock(lockName);
}
}else {
log.warn("Timeout while acquiring serviceLock:{}",lockName);
//加锁失败,如果设置了自定义处理,则执行
String customLockTimeoutStrategy = servicelock.customLockTimeoutStrategy();
if (StringUtil.isNotEmpty(customLockTimeoutStrategy)) {
return handleCustomLockTimeoutStrategy(customLockTimeoutStrategy, joinPoint);
}else{
//默认处理
servicelock.lockTimeoutStrategy().handler(lockName);
}
return joinPoint.proceed();
}
}lockInfoHandleFactory.getLockInfoHandle(LockInfoType.SERVICE_LOCK)
public class LockInfoHandleFactory implements ApplicationContextAware {
private ApplicationContext applicationContext;
public LockInfoHandle getLockInfoHandle(String lockInfoType){
return applicationContext.getBean(lockInfoType,LockInfoHandle.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}LockInfoHandleFactory
LockInfoHandleFactory是锁信息工厂,返回的有分布式锁解析处理器 和 幂等解析处理器 ,其实 分布式锁 和 幂等 都依赖公共组件,因为两者都是靠Redssion来实现的,公共模块:
<dependency>
<groupId>com.example</groupId>
<artifactId>damai-redisson-common-framework</artifactId>
<version>${revision}</version>
</dependency>LockInfoHandle
LockInfoHandle是解析器的抽象层
public interface LockInfoHandle {
/**
* 获取锁信息
* @param joinPoint 切面
* @param name 锁业务名
* @param keys 锁
* @return 锁信息
* */
String getLockName(JoinPoint joinPoint, String name, String[] keys);
/**
* 拼装锁信息
* @param name 锁业务名
* @param keys 锁
* @return 锁信息
* */
String simpleGetLockName(String name,String[] keys);
}在LockInfoHandle是解析器的抽象层下,还有一层抽象实现层AbstractLockInfoHandle,来实现公共部分
AbstractLockInfoHandle
public abstract class AbstractLockInfoHandle implements LockInfoHandle {
private static final String LOCK_DISTRIBUTE_ID_NAME_PREFIX = "LOCK_DISTRIBUTE_ID";
private final ParameterNameDiscoverer nameDiscoverer = new ExtParameterNameDiscoverer();
private final ExpressionParser parser = new SpelExpressionParser();
/**
* 锁信息前缀
* @return 具体前缀
* */
protected abstract String getLockPrefixName();
/**
* 解析出锁的键
* @param joinPoint 切点
* @param name 业务名
* @param keys 参数值
* @return 解析后的锁的键
*
* */
@Override
public String getLockName(JoinPoint joinPoint,String name,String[] keys){
return SpringUtil.getPrefixDistinctionName() + "-" + getLockPrefixName() + SEPARATOR + name + getRelKey(joinPoint, keys);
}
/**
* 解析出锁的键
* @param name 业务名
* @param keys 参数名
* @return 解析后的锁的键
*
* */
@Override
public String simpleGetLockName(String name,String[] keys){
List<String> definitionKeyList = new ArrayList<>();
for (String key : keys) {
if (StringUtil.isNotEmpty(key)) {
definitionKeyList.add(key);
}
}
return SpringUtil.getPrefixDistinctionName() + "-" +
LOCK_DISTRIBUTE_ID_NAME_PREFIX + SEPARATOR + name + SEPARATOR + String.join(SEPARATOR, definitionKeyList);
}
/**
* 获取自定义键
* @param joinPoint 切点
* @param keys 参数名
* @return 获取自定义键
* */
private String getRelKey(JoinPoint joinPoint, String[] keys){
Method method = getMethod(joinPoint);
List<String> definitionKeys = getSpElKey(keys, method, joinPoint.getArgs());
return SEPARATOR + String.join(SEPARATOR, definitionKeys);
}
/**
* 获取自定义键
* @param joinPoint 切点
* @return 切点的方法
* */
private Method getMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (method.getDeclaringClass().isInterface()) {
try {
method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(),
method.getParameterTypes());
} catch (Exception e) {
log.error("get method error ",e);
}
}
return method;
}
/**
* 获取自定义键
* @param definitionKeys 参数名
* @param method 方法
* @param parameterValues 参数值
* @return 切点的方法
* */
private List<String> getSpElKey(String[] definitionKeys, Method method, Object[] parameterValues) {
List<String> definitionKeyList = new ArrayList<>();
for (String definitionKey : definitionKeys) {
if (!ObjectUtils.isEmpty(definitionKey)) {
EvaluationContext context = new MethodBasedEvaluationContext(null, method, parameterValues, nameDiscoverer);
Object objKey = parser.parseExpression(definitionKey).getValue(context);
definitionKeyList.add(ObjectUtils.nullSafeToString(objKey));
}
}
return definitionKeyList;
}
}ServiceLockInfoHandle 分布式锁的实现
public class ServiceLockInfoHandle extends AbstractLockInfoHandle {
private static final String LOCK_PREFIX_NAME = "SERVICE_LOCK";
@Override
protected String getLockPrefixName() {
return LOCK_PREFIX_NAME;
}
}RepeatExecuteLimitLockInfoHandle 防重复幂等的实现
public class RepeatExecuteLimitLockInfoHandle extends AbstractLockInfoHandle {
public static final String PREFIX_NAME = "REPEAT_EXECUTE_LIMIT";
@Override
protected String getLockPrefixName() {
return PREFIX_NAME;
}
}看到这里如果迷糊了不用着急,下面会按照执行流程来逐步分析,这样就好理解了,还是获得切面的逻辑中
LockInfoHandle lockInfoHandle = lockInfoHandleFactory.getLockInfoHandle(LockInfoType.SERVICE_LOCK);这时获取的LockInfoHandle实际是ServiceLockInfoHandle,然后执行的就是分析获取锁的名字
String lockName = lockInfoHandle.getLockName(joinPoint, servicelock.name(),servicelock.keys());lockInfoHandle.getLockName
/**
* 锁信息前缀
* @return 具体前缀
* */
protected abstract String getLockPrefixName();
/**
* 解析出锁的键
* @param joinPoint 切点
* @param name 业务名
* @param keys 参数值
* @return 解析后的锁的键
*
* */
@Override
public String getLockName(JoinPoint joinPoint,String name,String[] keys){
return SpringUtil.getPrefixDistinctionName() + "-" + getLockPrefixName() + SEPARATOR + name + getRelKey(joinPoint, keys);
}getLockName就是抽象类AbstractLockInfoHandle中的方法,而getLockPrefixName的抽象方法,作用是获得锁的类型名字作为前缀,需要具体的分布式锁和幂等的实现类来实现,来看下分布式锁的实现
public class ServiceLockInfoHandle extends AbstractLockInfoHandle {
private static final String LOCK_PREFIX_NAME = "SERVICE_LOCK";
@Override
protected String getLockPrefixName() {
return LOCK_PREFIX_NAME;
}
}当获取锁的前缀名后,接下来执行**getRelKey(joinPoint, keys)**通过spel根据的入参来替换成真正的值
/**
* 获取自定义键
* @param joinPoint 切点
* @param keys 参数名
* @return 获取自定义键
* */
private String getRelKey(JoinPoint joinPoint, String[] keys){
Method method = getMethod(joinPoint);
List<String> definitionKeys = getSpElKey(keys, method, joinPoint.getArgs());
return SEPARATOR + String.join(SEPARATOR, definitionKeys);
}**getMethod(joinPoint)**来获取当前所在的方法
/**
* 获取自定义键
* @param joinPoint 切点
* @return 切点的方法
* */
private Method getMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (method.getDeclaringClass().isInterface()) {
try {
method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(),
method.getParameterTypes());
} catch (Exception e) {
log.error("get method error ",e);
}
}
return method;
}**getSpElKey(keys, method, joinPoint.getArgs())**就是真正的解析入参值了
/**
* 获取自定义键
* @param definitionKeys 参数名
* @param method 方法
* @param parameterValues 参数值
* @return 切点的方法
* */
private List<String> getSpElKey(String[] definitionKeys, Method method, Object[] parameterValues) {
List<String> definitionKeyList = new ArrayList<>();
for (String definitionKey : definitionKeys) {
if (!ObjectUtils.isEmpty(definitionKey)) {
//spEl的构建
EvaluationContext context = new MethodBasedEvaluationContext(null, method, parameterValues, nameDiscoverer);
//解析参数名来替换成真正的参数值
Object objKey = parser.parseExpression(definitionKey).getValue(context);
definitionKeyList.add(ObjectUtils.nullSafeToString(objKey));
}
}
//将解析出的参数值拼装一起后返回
return definitionKeyList;
}执行到这里,**String lockName = lockInfoHandle.getLockName(joinPoint, servicelock.name(),servicelock.keys())**继续锁的名字这一流程就完成了,下面接着介绍
LockType lockType = servicelock.lockType()
获取锁的类型,类型包括Reentrant 可重入锁(默认)、Fair公平锁、Read读锁、Write写锁
long waitTime = servicelock.waitTime();
尝试加锁失败最多等待时间,默认10s
TimeUnit timeUnit = servicelock.timeUnit();
获取等待时间的时间单位,默认秒
ServiceLocker lock = serviceLockFactory.getLock(lockType);
根据锁的类型从锁工厂中获取真正的锁
@AllArgsConstructor
public class ServiceLockFactory {
private final ManageLocker manageLocker;
public ServiceLocker getLock(LockType lockType){
ServiceLocker lock;
switch (lockType) {
case Fair:
lock = manageLocker.getFairLocker();
break;
case Write:
lock = manageLocker.getWriteLocker();
break;
case Read:
lock = manageLocker.getReadLocker();
break;
default:
lock = manageLocker.getReentrantLocker();
break;
}
return lock;
}
}这里我们使用默认的重入锁RedissonReentrantLocker
RedissonReentrantLocker
@AllArgsConstructor
public class RedissonReentrantLocker implements ServiceLocker {
private final RedissonClient redissonClient;
@Override
public RLock getLock(String lockKey) {
return redissonClient.getFairLock(lockKey);
}
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
@Override
public RLock lock(String lockKey, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}
@Override
public RLock lock(String lockKey, TimeUnit unit ,long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, unit);
return lock;
}
@Override
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, unit);
} catch (InterruptedException e) {
return false;
}
}
@Override
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.unlock();
}
}boolean result = lock.tryLock(lockName, timeUnit, waitTime);
进行加锁
joinPoint.proceed()
执行业务逻辑后解锁
if (result) {
try {
return joinPoint.proceed();
}finally{
lock.unlock(lockName);
}
}servicelock.lockTimeoutStrategy()
没有获得锁后执行快速失败或者自定义策略
else {
log.warn("Timeout while acquiring serviceLock:{}",lockName);
//加锁失败,如果设置了自定义处理,则执行
String customLockTimeoutStrategy = servicelock.customLockTimeoutStrategy();
if (StringUtil.isNotEmpty(customLockTimeoutStrategy)) {
//执行自定义处理
return handleCustomLockTimeoutStrategy(customLockTimeoutStrategy, joinPoint);
}else{
//默认处理 快速失败
servicelock.lockTimeoutStrategy().handler(lockName);
}
return joinPoint.proceed();
}public enum LockTimeOutStrategy implements LockTimeOutHandler{
/**
* 快速失败
* */
FAIL(){
@Override
public void handler(String lockName) {
String msg = String.format("%s请求频繁",lockName);
throw new RuntimeException(msg);
}
}
}- 如果自定义策略处理存在,则执行,否则执行快速失败
- 快速失败的方案为直接抛出异常
handleCustomLockTimeoutStrategy
public Object handleCustomLockTimeoutStrategy(String customLockTimeoutStrategy,JoinPoint joinPoint) {
// prepare invocation context
Method currentMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
Object target = joinPoint.getTarget();
Method handleMethod = null;
try {
handleMethod = target.getClass().getDeclaredMethod(customLockTimeoutStrategy, currentMethod.getParameterTypes());
handleMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Illegal annotation param customLockTimeoutStrategy :" + customLockTimeoutStrategy,e);
}
Object[] args = joinPoint.getArgs();
// invoke
Object result;
try {
result = handleMethod.invoke(target, args);
} catch (IllegalAccessException e) {
throw new RuntimeException("Fail to illegal access custom lock timeout handler: " + customLockTimeoutStrategy ,e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Fail to invoke custom lock timeout handler: " + customLockTimeoutStrategy ,e);
}
return result;
}customLockTimeoutStrategy就是自定义处理的方法名,入参的数量和类型必须和加锁的方法完全一致,然后利用切面时的方法参数,传入到自定义处理的方法中,利用反射的原理来执行自定义的方法
到这里是把分布式锁切面的实现介绍完毕,虽然篇幅有点长,如果认真梳理的话,其实还是很清晰的,接下来我们来介绍分布式锁方法级别的实现流程,跳转到相关文档查询
更新: 2025-10-13 11:51:01
原文: https://www.yuque.com/u22210564/ykdrdh/ag86c75wkxdhuz44