博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于注解的redis的分布式锁实现
阅读量:4208 次
发布时间:2019-05-26

本文共 6647 字,大约阅读时间需要 22 分钟。

基于redis的分布式锁实现的优化

上篇的redis的分布式锁实现,但还是过于繁杂,代码侵入性高

*本遍是将上篇的实现调整成注解方式,利用aop实现分布式锁

只用在方法上加个注解,同时加上了重试机制
*

1.列子

@RedisLock(lockPrefix = AbstractRedisContants.DIST_LOCK_FUND, lockParameter = "fundId")    public void handle(FundAmountOptTypeEnum optType, Long fundId, BigDecimal amount, String operator, String remark) {        strategyMap.get(optType).handle(fundId, amount, operator, remark);    }

2.RedisLock

import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.annotation.Order;/** * redis锁注解 *  * @author qyl * @date 2018/12/27 */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})@Documented@Order(value = 10)public @interface RedisLock {    // 锁前缀    String lockPrefix() default "";    // 方法参数名(用于取参数名的值与锁前缀拼接成锁名),尽量不要用对象map等,对象会toString后与锁前缀拼接    String lockParameter() default "";    // 尝试加锁,最多等待时间(毫秒)    long lockWait() default 3000L;    // 自动解锁时间 (毫秒)    long autoUnlockTime() default 10000L;    // 重试次数    int retryNum() default 0;    // 重试等待时间 (毫秒)    long retryWait() default 500L;}

@Order(value = 10)

当使用了@Transactional 或其它切面时,相当于在执行执行多次次AOP切面。那么我们需要通过order 属性去定义AOP切面的先后执行顺序。 order越小,在AOP的chain 中越靠前,越先执行。(chain模式)

3.RedisLockAspect

import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import java.util.concurrent.TimeUnit;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.redisson.api.RLock;import org.redisson.api.RedissonClient;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import org.springframework.util.Assert;import com.feitai.jieya.server.common.annotation.RedisLock;import com.feitai.jieya.server.common.exception.BusinessException;import com.feitai.jieya.server.utils.StringUtils;import lombok.extern.slf4j.Slf4j;/** * Description: 分布式锁 * 

* 先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试) 如果没有重试次数,方法返回null 记得捕获NP 当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔 *

* @author qyl * */@Aspect@Component@Slf4j@Order(10)public class RedisLockAspect { private static final String LOCK_NAME = "lockName"; private static final String lOCK_WAIT = "lockWait"; private static final String AUTO_UNLOCK_TIME = "autoUnlockTime"; private static final String RETRY_NUM = "retryNum"; private static final String RETRY_WAIT = "retryWait"; /** * redis工具类 */ @Autowired private RedissonClient redissonClient; @Pointcut("@annotation(com.feitai.jieya.server.common.annotation.RedisLock)") public void lockAspect() {} @Around("lockAspect()") public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable { // 获取注解中的参数 Map
annotationArgs = this.getAnnotationArgs(proceeding); String lockName = (String)annotationArgs.get(LOCK_NAME); Assert.notNull(lockName, "分布式,锁名不能为空"); int retryNum = (int)annotationArgs.get(RETRY_NUM); long retryWait = (long)annotationArgs.get(RETRY_WAIT); long lockWait = (long)annotationArgs.get(lOCK_WAIT); long autoUnlockTime = (long)annotationArgs.get(AUTO_UNLOCK_TIME); // 获取锁 RLock lock = redissonClient.getLock(lockName); try { boolean res = lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS); if (res) { // 执行主逻辑 return proceeding.proceed(); } else { // 如果重试次数为零, 则不重试 if (retryNum <= 0) { log.info(String.format("{%s}已经被锁, 不重试", lockName)); throw new BusinessException(String.format("{%s}已经被锁, 不重试", lockName)); } if (retryWait == 0) { retryWait = 200L; } // 设置失败次数计数器, 当到达指定次数时, 返回失败 int failCount = 1; while (failCount <= retryNum) { // 等待指定时间ms Thread.sleep(retryWait); if (lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS)) { // 执行主逻辑 return proceeding.proceed(); } else { log.info(String.format("{%s}已经被锁, 正在重试[ %s/%s ],重试间隔{%s}毫秒", lockName, failCount, retryNum, retryWait)); failCount++; } } throw new BusinessException("系统繁忙, 请稍等再试"); } } catch (Throwable throwable) { log.error(String.format("执行分布式锁发生异常锁名:{%s},异常名称:{%s}", lockName, throwable.getMessage())); throw throwable; } finally { lock.unlock(); } } /** * 获取锁参数 * * @param proceeding * @return */ private Map
getAnnotationArgs(ProceedingJoinPoint proceeding) { // if (!(objs[i] instanceof ExtendedServletRequestDataBinder) // && !(objs[i] instanceof HttpServletResponseWrapper)) { proceeding.getArgs(); Object[] objs = proceeding.getArgs(); String[] argNames = ((MethodSignature)proceeding.getSignature()).getParameterNames(); // 参数名 Class target = proceeding.getTarget().getClass(); Method[] methods = target.getMethods(); String methodName = proceeding.getSignature().getName(); for (Method method : methods) { if (method.getName().equals(methodName)) { Map
result = new HashMap
(); RedisLock redisLock = method.getAnnotation(RedisLock.class); if (StringUtils.isNotBlank(redisLock.lockParameter())) { for (int i = 0; i < objs.length; i++) { if (redisLock.lockParameter().equals(argNames[i])) { result.put(LOCK_NAME, redisLock.lockPrefix() + objs[i]); break; } } } else { result.put(LOCK_NAME, redisLock.lockPrefix()); } result.put(lOCK_WAIT, redisLock.lockWait()); result.put(AUTO_UNLOCK_TIME, redisLock.autoUnlockTime()); result.put(RETRY_NUM, redisLock.retryNum()); result.put(RETRY_WAIT, redisLock.retryWait()); return result; } } throw new RuntimeException("异常"); }

转载地址:http://kwrli.baihongyu.com/

你可能感兴趣的文章
【Error】make LKM时 找不到符号
查看>>
【转载】【C语言】浅析C语言之uint8_t / uint16_t / uint32_t /uint64_t
查看>>
【maven】打包jar上传到服务器运行
查看>>
关闭centos wayland
查看>>
【Error】chsh: PAM: Authentication failure
查看>>
【Error】zsh历史记录丢失
查看>>
解析漏洞总结
查看>>
【Windows C++】调用powershell上传指定目录下所有文件
查看>>
kotlin-android-extensions 插件无效问题
查看>>
经典排序算法--Java实现
查看>>
Java中JRadioButton单选按钮分组方法
查看>>
Java图形界面中单选按钮JRadioButton和按钮Button事件处理
查看>>
小练习 - 排序:冒泡、选择、快排
查看>>
SparkStreaming 如何保证消费Kafka的数据不丢失不重复
查看>>
Spark Shuffle及其调优
查看>>
数据仓库分层
查看>>
常见数据结构-TrieTree/线段树/TreeSet
查看>>
Hive数据倾斜
查看>>
TopK问题
查看>>
Hive调优
查看>>