分布式-技术专区-Redis分布式锁实现-第二步
1.利用aop实现分布式锁
2.只用在方法上加个注解,同时加上了重试机制
1.前提我们可以个性化定制一些注解例如:RedisLock注解操作
@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锁注解
* @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: 分布式锁
* <p>
* 先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试) 如果没有重试次数,方法返回null 记得捕获NP 当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔
* </p>
*/
@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<String, Object> 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<String, Object> 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<String, Object> result = new HashMap<String, Object>();
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("异常");}
分布式-技术专区-Redis分布式锁实现-第二步的更多相关文章
- 分布式-技术专区-Redis分布式锁实现-第一步
承接前面一篇Redis分布式锁的原理介绍 https://www.cnblogs.com/liboware/p/11921759.html 我们针对于实现方案进行接下来上篇进行重新的规划和定义以及完善 ...
- 分布式-技术专区-Redis分布式锁原理实现
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天来探讨分布式锁这个话题. ...
- 分布式-技术专区-Redis并发竞争key的解决方案详解
Redis缓存的高性能有目共睹,应用的场景也是非常广泛,但是在高并发的场景下,也会出现问题:缓存击穿.缓存雪崩.缓存和数据一致性,以及今天要谈到的缓存并发竞争.这里的并发指的是多个redis的clie ...
- 分布式-技术专区-Redis和MySQL缓存一致性问题
1.Redis 缓存和 MySQL 数据如何实现一致性 需求起因 缓存和数据库一致性解决方案 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操 ...
- 搞懂分布式技术12:分布式ID生成方案
搞懂分布式技术12:分布式ID生成方案 ## 转自: 58沈剑 架构师之路 2017-06-25 一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-i ...
- 搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法
搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法 2PC 由于BASE理论需要在一致性和可用性方面做出权衡,因此涌现了很多关于一致性的算法和协议.其中比较著名的有二阶提交协议(2 Phas ...
- 搞懂分布式技术11:分布式session解决方案与一致性hash
搞懂分布式技术11:分布式session解决方案与一致性hash session一致性架构设计实践 原创: 58沈剑 架构师之路 2017-05-18 一.缘起 什么是session? 服务器为每个用 ...
- fourinone分布式缓存研究和Redis分布式缓存研究
最近在写一个天气数据推送的项目,准备用缓存来存储数据.下面分别介绍一下fourinone分布式缓存和Redis分布式缓存,然后对二者进行对比,以供大家参考. 1 fourinone分布式缓存特性 1 ...
- 如何正确使用redis分布式锁
前言 笔者在公司担任技术面试官,在笔者面试过程中,如果面试候选人提到了reids分布式锁,笔者都会问一下redis分布式锁的知识点,但是令笔者遗憾的是,该知识点十个人中有九个人都答得不清楚,或者回 ...
随机推荐
- Neo4j百万级数据导入只需30s
先上图:425万nodes.180万relationships只用了30s 243ms 项目需要生成关系图,开始考虑的是用Neo4j官网提供的REST API,从solr中查出2组数据先创建节点再创建 ...
- mysql -- mysql基于ssl的主从复制
mysql基于ssl的主从复制由于mysql在复制过程中是明文的,所以就大大降低了安全性,因此需要借助于ssl加密来增加其复制的安全性. 主服务器node1:172.16.200.1从服务器node2 ...
- Linux Shell脚本在service模式下的环境变量
普通Shell脚本在终端下运行时,环境变量来自于init/profile/.bash_profile等,就是在当前终端下可以得到的环境,比如PATH.在service模式下,脚本运行在受限的环境中:比 ...
- go语言从例子开始之Example20.错误处理
Go 语言使用一个独立的·明确的返回值来传递错误信息的.这与使用异常的 Java 和 Ruby 以及在 C 语言中经常见到的超重的单返回值/错误值相比,Go 语言的处理方式能清楚的知道哪个函数返回了错 ...
- Tomcat8.0源码编译
最近打算开始研究一下Tomcat的工作原理,拜读一下源码.所以先从编译源码开始了.尽管网上有那么多的资料,但是总是觉得,自己研究一遍,写一遍,在动手做一遍能够让我们更加深入的了解.现在整个社会都流行着 ...
- 转帖 移动前端开发之viewport的深入理解
在移动设备上进行网页的重构或开发,首先得搞明白的就是移动设备上的viewport了,只有明白了viewport的概念以及弄清楚了跟viewport有关的meta标签的使用,才能更好地让我们的网页适配或 ...
- 3、selenium 问题汇总
一.'chromedriver' executable needs to be in PAT: 解决方法 下载谷歌驱动文件:Chromedriver.exe 将Chromedriver.exe 拷贝 ...
- ansible_playbook语法中的循环语句归纳
种类一.标准循环添加多个用户 - name: add several users user: name={{ item }} state=present groups=wheel with_items ...
- OC学习篇之---循环引用问题
在之前的一片文章中,我们介绍了数组操作对象的时候引用问题以及自动释放池的概念: http://blog.csdn.net/jiangwei0910410003/article/details/4192 ...
- 2019牛客多校第五场 B - generator 1 矩阵快速幂+十倍增+二进制倍增优化
B - generator 1 题意 给你\(x_{0}.x_{1}.a.b.b.mod\),根据\(x_{i} = a*x_{i-1} + b*x_{i-2}\)求出\(x_{n}\) 思路 一般看 ...