分布式-技术专区-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分布式锁的知识点,但是令笔者遗憾的是,该知识点十个人中有九个人都答得不清楚,或者回 ...
随机推荐
- 后台得到jsp提交name属性相同的内容
直接上代码: req.setCharacterEncoding("utf-8");String [] node = req.getParameterValues("nod ...
- shell条件测试举例
- IPv6 关于路由器配置静态IPv6路由的命令
今天在学习路由器配置ipv6 的时候遇到了一点疑惑 一条命令为:ipv6 route FE80:0202::/32 serail 0/1 201 一条命令为:ipv6 route FE80:0202: ...
- SpringMvc支持Ajax概述【见前两篇随笔--详述前后数据互通】
1.原生javaWeb:不再用 1).导入GSON: 2).返回的数据用GSON转成json 3).写出去: 2.SpringMVC快速的完成ajax功能? 导包 jackson-annotation ...
- vue video全屏播放
需求: 1.视频为长方形,页面初始化打开为横屏全屏播放视频. 2.微信不支持自动播放,故自动播放需求删除. 方法: 1.vue-video-player插件 因需求较简单,仅要求播放本地一个视频,故未 ...
- python plotly 使用教程
1.plotly介绍 lotly的Python图形库使互动的出版质量图表成为在线. 如何制作线图,散点图,面积图,条形图,误差线,箱形图,直方图,热图,子图,多轴,极坐标图和气泡图的示例. 推荐最好使 ...
- 树莓派GPIO口驱动编写
一.wiringpi写法 #include <wiringPi.h> #include <stdlib.h> int main(int argc,char *argv[]) { ...
- redis集群-4
redis集群原理 redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态.每个节点都和其 ...
- 为什么要用getBaseContext()方法代替this?(转)
问:this 常常引用当前的 context.但是有些时候,必须使用getBaseContext()来代替this.就是说使用this会引发错误. 如下面的例子: Spinner spinner = ...
- python爬虫https://www.imdb.com/chart/top的电影
目标:爬取https://www.imdb.com/chart/top网页上面的电影top20 直接上main.py代码: #!/usr/bin/python35 # -*- coding:utf-8 ...