Skip to content

组件讲解-分布式锁原理的详细剖析-上

本文将详细介绍分布式锁的设计架构,其中应用了大量的设计模式,和Spring的自动装配,相信小伙伴看完后会对架构组件的设计有更深入的理解

依赖

xml
<dependency>
    <groupId>com.example</groupId>
    <artifactId>damai-service-lock-framework</artifactId>
    <version>${revision}</version>
</dependency>

ServiceLockAutoConfiguration

java
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

锁管理缓存,这里将重入锁、公平锁、写锁、读锁的实例给缓存起来

java
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

下面我们来分析具体的切面执行逻辑

java
@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)

java
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来实现的,公共模块:

xml
<dependency>
    <groupId>com.example</groupId>
    <artifactId>damai-redisson-common-framework</artifactId>
    <version>${revision}</version>
</dependency>

LockInfoHandle

LockInfoHandle是解析器的抽象层

java
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

java
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 分布式锁的实现

java
public class ServiceLockInfoHandle extends AbstractLockInfoHandle {

    private static final String LOCK_PREFIX_NAME = "SERVICE_LOCK";
    
    @Override
    protected String getLockPrefixName() {
        return LOCK_PREFIX_NAME;
    }
}

RepeatExecuteLimitLockInfoHandle 防重复幂等的实现

java
public class RepeatExecuteLimitLockInfoHandle extends AbstractLockInfoHandle {

    public static final String PREFIX_NAME = "REPEAT_EXECUTE_LIMIT";
    
    @Override
    protected String getLockPrefixName() {
        return PREFIX_NAME;
    }
}

看到这里如果迷糊了不用着急,下面会按照执行流程来逐步分析,这样就好理解了,还是获得切面的逻辑中

java
LockInfoHandle lockInfoHandle = lockInfoHandleFactory.getLockInfoHandle(LockInfoType.SERVICE_LOCK);

这时获取的LockInfoHandle实际是ServiceLockInfoHandle,然后执行的就是分析获取锁的名字

java
String lockName = lockInfoHandle.getLockName(joinPoint, servicelock.name(),servicelock.keys());

lockInfoHandle.getLockName

java
/**
 * 锁信息前缀
 * @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的抽象方法,作用是获得锁的类型名字作为前缀,需要具体的分布式锁和幂等的实现类来实现,来看下分布式锁的实现

java
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根据的入参来替换成真正的值

java
/**
 * 获取自定义键
 * @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)**来获取当前所在的方法

java
/**
 * 获取自定义键
 * @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())**就是真正的解析入参值了

java
/**
 * 获取自定义键
 * @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);

根据锁的类型从锁工厂中获取真正的锁

java
@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

java
@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()

执行业务逻辑后解锁

java
if (result) {
    try {
        return joinPoint.proceed();
    }finally{
        lock.unlock(lockName);
    }
}

servicelock.lockTimeoutStrategy()

没有获得锁后执行快速失败或者自定义策略

java
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();
}
java
public enum LockTimeOutStrategy implements LockTimeOutHandler{
    /**
     * 快速失败
     * */
    FAIL(){
        @Override
        public void handler(String lockName) {
            String msg = String.format("%s请求频繁",lockName);
            throw new RuntimeException(msg);
        }
    }
}
  • 如果自定义策略处理存在,则执行,否则执行快速失败
  • 快速失败的方案为直接抛出异常

handleCustomLockTimeoutStrategy

java
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

Java 后端面试知识库