【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(一)https://developer.aliyun.com/article/1471009
定义我们分布式锁的基础抽象类
接下来我们就定一下我们分布式锁的一个基础抽象类:AbstractDistributeLockSupport。这个类主要实现了我们之前的那个接口DistributeLockSupport。
java
复制代码
public abstract class AbstractDistributeLockSupport<T> implements DistributeLockSupport<T> { /** * 检验参数 * @param distributeLockParam * @return */ protected DistributeLockParam fullDistributeDefaultValue(DistributeLockParam distributeLockParam){ Preconditions.checkNotNull(distributeLockParam,"检测到了参数不允许为空!"); DistributeLockType distributeLockType = distributeLockParam.getLockType(); distributeLockParam.setLockType(Optional.ofNullable(distributeLockType).orElse(DistributeLockType.FAIR_LOCK)); distributeLockParam.setExpireTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_EXPIRE_TIME)); distributeLockParam.setWaitTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_WAIT_TIME)); distributeLockParam.setTimeUnit(Optional.ofNullable(distributeLockParam.getTimeUnit()).orElse(TimeUnit.SECONDS)); return distributeLockParam; } /** * 构建相关的锁key值 * @param distributeLockParam * @return */ protected String buildLockKey(DistributeLockParam distributeLockParam){ String lockId = StringUtils.defaultIfEmpty(distributeLockParam.getLockUUid(), UUID.fastUUID().toString()); distributeLockParam.setLockUUid(lockId); String delmiter = StringUtils.defaultIfEmpty(distributeLockParam.getDelimiter(), DEFAULT_DELIMTER); distributeLockParam.setDelimiter(delmiter); String prefix = StringUtils.defaultIfEmpty(distributeLockParam .getLockNamePrefix(),DEFAULT_KEY_PREFIX); distributeLockParam.setLockNamePrefix(prefix); String lockFullName = ""; if(!delmiter.equals(DEFAULT_DELIMTER)){ //todo 待优化 Joiner joiner = Joiner.on(delmiter).skipNulls(); lockFullName = joiner.join(prefix,lockId); }else{ lockFullName = DEFAULT_JOINER.join(prefix,lockId); } return lockFullName; }
该类主要包含两个方法。分别是fullDistributeDefaultValue和buildLockKey。
fullDistributeDefaultValue方法
这个方法主要的目的是为了校验以及填充一些我们没有写的参数的默认值。
java
复制代码
protected DistributeLockParam fullDistributeDefaultValue(DistributeLockParam distributeLockParam){ Preconditions.checkNotNull(distributeLockParam,"检测到了参数不允许为空!"); DistributeLockType distributeLockType = distributeLockParam.getLockType(); distributeLockParam.setLockType(Optional.ofNullable(distributeLockType).orElse(DistributeLockType.FAIR_LOCK)); distributeLockParam.setExpireTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_EXPIRE_TIME)); distributeLockParam.setWaitTime(Optional.ofNullable(distributeLockParam.getExpireTime()).orElse(DEFAULT_WAIT_TIME)); distributeLockParam.setTimeUnit(Optional.ofNullable(distributeLockParam.getTimeUnit()).orElse(TimeUnit.SECONDS)); return distributeLockParam; }
buildLockKey方法
该类主要负责的是构建我们的分布式锁的key。
java
复制代码
protected String buildLockKey(DistributeLockParam distributeLockParam){ String lockId = StringUtils.defaultIfEmpty(distributeLockParam.getLockUUid(), UUID.fastUUID().toString()); distributeLockParam.setLockUUid(lockId); String delmiter = StringUtils.defaultIfEmpty(distributeLockParam.getDelimiter(), DEFAULT_DELIMTER); distributeLockParam.setDelimiter(delmiter); String prefix = StringUtils.defaultIfEmpty(distributeLockParam .getLockNamePrefix(),DEFAULT_KEY_PREFIX); distributeLockParam.setLockNamePrefix(prefix); String lockFullName = ""; if(!delmiter.equals(DEFAULT_DELIMTER)){ //todo 待优化 Joiner joiner = Joiner.on(delmiter).skipNulls(); lockFullName = joiner.join(prefix,lockId); }else{ lockFullName = DEFAULT_JOINER.join(prefix,lockId); } return lockFullName; }
从在马上可以看出来,他主要就是将我们之前的那些所有的属性进行连接拼接到一起。
至此,我们的基础组建部分的抽象部分就已经完成了。那么接下来呢我们需要进行一个实现Redis模式下的分布式锁。
定义Redis分布式锁的注解
主要用于AOP的一个拦截以及获取一些特定化的参数。
RedisDistributedLock注解
java
复制代码
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface RedisDistributedLock { String prefix() default ""; /** * 锁过期时间 */ int expireTime() default 30; /** * 获取锁等待时间 */ int waitTime() default 10; TimeUnit timeUnit() default TimeUnit.SECONDS; String delimiter() default ":"; LockCategory category() default LockCategory.COMMON; }
RedisDistributedLockParam注解
主要用于参数方法上的修饰,获取参数相关的一些主要用于参数方法上的修饰,获取参数相关的一些参数值作为我们的分布式主键的key。
java
复制代码
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface RedisDistributedLockParam { String name() default ""; }
实现分布式锁的唯一键的生成器
我们定义分布式锁组件抽象接口的redis版本为RedisDistributedLockKeyGenerator。
java
复制代码
public class RedisDistributedLockKeyGenerator implements LockKeyGenerator { @Override public String getLockKey(ProceedingJoinPoint pjp) { MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); RedisDistributedLock lockAnnotation = method.getAnnotation(RedisDistributedLock.class); final Object[] args = pjp.getArgs(); final Parameter[] parameters = method.getParameters(); StringBuilder builder = new StringBuilder(); // 默认解析方法里面带 CacheParam 注解的属性,如果没有尝试着解析实体对象中的 for (int i = 0; i < parameters.length; i++) { final RedisDistributedLockParam annotation = parameters[i].getAnnotation(RedisDistributedLockParam.class); if (annotation == null) { continue; } builder.append(lockAnnotation.delimiter()).append(args[i]); } if (StringUtils.isEmpty(builder.toString())) { final Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 0; i < parameterAnnotations.length; i++) { final Object object = args[i]; final Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { final RedisDistributedLockParam annotation = field.getAnnotation(RedisDistributedLockParam.class); if (annotation == null) { continue; } field.setAccessible(true); builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object)); } } } return lockAnnotation.prefix() + builder.toString(); } }
上面的码主要是用鱼上面的代码主要是用鱼去提取注解上的参数以及一些呃参数的key的一个基本信息。
实现分布式锁实现类RedisDistributeLockSupport
RedisDistributeLockSupport主要实现了我们的抽象分布式锁的核心业务接口。
其中使用了Redisson的RedissonClient客户端服务,从而进行选择类型,进行选择分布式锁的方式。
java
复制代码
@Slf4j @Component public class RedisDistributeLockSupport extends AbstractDistributeLockSupport<RLock> { @Autowired RedissonClient redissonClient; /** * 非阻塞方式锁 * @param distributeLockParam * @return */ @Override public RLock lock(DistributeLockParam distributeLockParam) { distributeLockParam = fullDistributeDefaultValue(distributeLockParam); String lockKey = buildLockKey(distributeLockParam); RLock rLock = null; try { switch (distributeLockParam.getLockType()) { // 可重入锁 case REENTRANT_LOCK: { rLock = redissonClient.getLock(lockKey); break; } // 非公平锁 case FAIR_LOCK: { rLock = redissonClient.getFairLock(lockKey); break; } default: { throw new UnsupportedOperationException("暂时不支持此种方式的锁!"); } } Boolean result = rLock.tryLock(distributeLockParam.getWaitTime(), distributeLockParam.getExpireTime(), distributeLockParam.getTimeUnit()); return rLock; } catch (InterruptedException e) { log.error("加锁为阻塞模式下的锁进行失败!", e); return rLock; } } @Override public void unlock(RLock param, DistributeLockParam distributeLockParam) { try { param.unlock(); } catch (Exception e) { log.error("解我操!啊?锁为阻塞模式下的锁进行失败!", e); } } }
可以根据我们所的类型选择。公平锁或者是和重入锁。
实现分布式锁实现类RedisDistributedLockAspect切面类
java
复制代码
@Aspect @Order(4) @Slf4j public class RedisDistributedLockAspect { @Autowired private RedissonClient redissonClient; @Autowired private RedisDistributedLockKeyGenerator redisDistributedLockKeyGenerator; @Around("execution(public * *(..)) && @annotation(com.hyts.assemble.distributeLock.redis.RedisDistributedLock)") public Object interceptor(ProceedingJoinPoint pjp) throws Throwable { MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); RedisDistributedLock redisDistributedLock = method.getAnnotation(RedisDistributedLock.class); if (StringUtils.isEmpty(redisDistributedLock.prefix())) { throw new RuntimeException("lock key can't be null..."); } final String lockKey = redisDistributedLockKeyGenerator.getLockKey(pjp); RLock lock = chooseLock(redisDistributedLock,lockKey); //key不存在才能设置成功 Boolean success = null; Object proceed = null; try { success = lock.tryLock(redisDistributedLock.waitTime(), redisDistributedLock.expireTime(), redisDistributedLock.timeUnit()); if (success) { log.debug("tryLock success key [{}]", lockKey); proceed = pjp.proceed(); } else { log.error("key is : {" + lockKey + "} tryLock fail "); throw new RedisDistributedLockException(lockKey); } } catch (InterruptedException e) { log.error("key is : {" + lockKey + "} tryLock error ", e); throw new RedisDistributedLockException(lockKey, e.getMessage()); } finally { if (success) { log.debug("unlock [{}]", "key:" + lockKey); lock.unlock(); } } return proceed; } private RLock chooseLock(RedisDistributedLock redisDistributedLock, String lockName) { LockCategory category = redisDistributedLock.category(); switch (category) { case FAIR: return redissonClient.getFairLock(lockName); } return redissonClient.getLock(lockName); } }