分布式-技术专区-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分布式锁的知识点,但是令笔者遗憾的是,该知识点十个人中有九个人都答得不清楚,或者回 ...
随机推荐
- 创建调用Consul的客户端项目
创建调用Consul的客户端项目 创建项目 源码 Github仓库:https://github.com/sunweisheng/spring-cloud-example
- 对于一键退出APP功能实现的技术探讨
在Android的开发过程中,会经常存在“一键退出APP”的需求.经过一段时间的整理,其主要实现方式有以下几种. 本质:一键结束当前APP的所有activity&一键结束当前APP进程,两者合 ...
- [BOOKS]BIG DATA and DATA ANALYTICS: The Beginner's Guide to Understanding the Analytical World
- 如何在Mac上将视频刻录到DVD / ISO文件
如果您希望将喜爱的视频转换为DVD / Blu-ray光盘以进行物理备份或播放,则Mac版Wondershare UniConverter可以专业地完成任务.今天的教程就是如何在Mac上轻松刻录DVD ...
- magento 站内优化和站外优化详解
关于Magento如何如何我就不做评论了,一句话,谁用谁知道,搜索了下,百度和谷歌中文里还真没有一篇系统的关于magento seo的内容,花了一个上午的时间,稍微整理了一下,算是给新人一个指引吧.主 ...
- securityDemo依赖
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit ...
- TableStore最佳实践:轻松实现轨迹管理与地理围栏
摘要: 基于TableStore轻松实现亿量级轨迹管理与地理围栏 一.方案背景 轨迹管理系统日常生活中使用非常普遍,如外卖派送轨迹.快递物流流转.车辆定位轨迹等.该场景与地理位置管理类似,核心点与瓶颈 ...
- 因为看见,所以发现:QBotVariant谢绝落幕
互联网给人带来便捷的同时,其公开大量的资源也同样给恶意利用者带了便捷,越来越多公开的恶意程序源码降低了对外攻击.入侵的难度,使得安全问题愈加严重. 阿里云安全团队从今年5月份监测到一BOT家族,其样本 ...
- qt 学习(六) 数据库注册用户
做什么: 1 登陆按钮按下出现注册页面, 2 输入账号 判断是否可用 查询数据库,用户名是否已经注册 3 输入密码 判断密码格式 4 输入邮箱 判断邮箱格式 查询数据库,邮箱是否已经注册 ...
- POJ 3279 Fliptile (dfs+二进制)
Description Farmer John knows that an intellectually satisfied cow is a happy cow who will give more ...