Redisson实现分布式锁(二)
本次基于注解+AOP实现分布式锁(招式与前文基于注解切换多数据源相同),话不多说,直接上样例:
首先自定义注解:设计时需要考虑锁的一般属性:keys,最大等待时间,超时时间,时间单位。
package com.paic.phssp.springtest.redisson; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestLockable {
String[] key() default ""; long maximumWaiteTime() default 2000; long expirationTime() default 1000; TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
新建一个抽象请求拦截器,设计模式:装饰模式,父类决定整体流程,具体细节交给字类实现,便于解耦扩展。
package com.paic.phssp.springtest.redisson; import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors; public abstract class AbstractRequestLockInterceptor {
protected abstract Lock getLock(String key); protected abstract boolean tryLock(long waitTime, long leaseTime, TimeUnit unit, Lock lock) throws InterruptedException; private static final String[] removeSigs = new String[]{"#","{","}"}; @Around("@annotation(RequestLockable)")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
//获取连接点的方法签名对象
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod();
String methodName = signature.getName(); //获取连接点所在的目标对象
String targetName = point.getTarget().getClass().getName(); //获取连接点方法运行时的入参列表
Object[] arguments = point.getArgs(); if (method != null && method.isAnnotationPresent(RequestLockable.class)) {
RequestLockable requestLockable = method.getAnnotation(RequestLockable.class); String requestLockKey = getLockBySpellKey(method, targetName, methodName, requestLockable.key(), arguments); System.out.println(">>>>requestLockKey="+requestLockKey); Lock lock = this.getLock(requestLockKey); boolean isLock = this.tryLock(requestLockable.maximumWaiteTime(), requestLockable.expirationTime(),
requestLockable.timeUnit(), lock);
if (isLock) {
try {
return point.proceed();
} finally {
//释放锁资源
lock.unlock();
}
} else {
throw new RuntimeException("获取锁资源失败");
}
} //通过反射执行目标对象的连接点处的方法
return point.proceed();
} /**
* 组装lock key
*
* @param method
* @param targetName 对象名
* @param methodName 方法名
* @param keys 注解key
* @param arguments 方法参数
* @return
*/
private String getLockBySpellKey(Method method, String targetName, String methodName, String[] keys, Object[] arguments) {
StringBuilder lockKey = new StringBuilder();
lockKey.append("lock.").append(targetName).append(".").append(methodName); if (keys != null) {
//Joiner Guava包
//String keyStr = Joiner.on(".").skipNulls().join(keys);
String keyStr = Arrays.stream(keys).filter(Objects::nonNull).collect(Collectors.joining("."));
System.out.println("RequestLockable:keys="+keyStr); if (!StringUtils.isBlank(keyStr)) {
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameters = discoverer.getParameterNames(method); //用Spell方法容易出问题,所以这里加了一些限制
keyStr = creatSpellExpressionStr(keyStr,parameters,removeSigs); int length = parameters.length;
if(length > 0){
if(!hasParameters(keyStr,parameters)){
//不包含参数直接用keyStr
lockKey.append("#").append(keyStr);
}else{
//keyStr 是否包含参数名,如果包含可用Spell表达式
//用Spell方法容易出问题,所以这里加工下
keyStr = creatSpellExpressionStr(keyStr,parameters,removeSigs);
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
Map<String,Object> vMap = new HashMap<String,Object>();
for (int i = 0; i < length; i++) {
//key:方法参数名,val:值
vMap.put(parameters[i],arguments[i]);
}
((StandardEvaluationContext) context).setVariables(vMap); //eg:#{#proKey}.asd#{#proKey}fa.#{#proId}#{#proKey} -> product.asdproductfa.123product
Expression expression = parser.parseExpression(keyStr,new TemplateParserContext());
String keysValue = expression.getValue(context, String.class);
lockKey.append("#").append(keysValue);
}
}
}
}
return lockKey.toString();
} private boolean hasParameters(String lockKey,String[] parameters){
boolean hasFlag = false;
for(String str : parameters){
if(StringUtils.indexOf(lockKey,str) != -1){
hasFlag = true;
break;
}
}
return hasFlag;
} private String creatSpellExpressionStr(String lockKey,String[] parameters,String[] removeSigs){
//去掉#{}等字符
for(String sig : removeSigs){
lockKey = StringUtils.replace(lockKey,sig,"");
} for(String str : parameters){
String repStr = "#{#"+str+"}";
lockKey = StringUtils.replace(lockKey,str,repStr);
}
return lockKey;
} }
具体实现拦截类:
package com.paic.phssp.springtest.redisson; import org.aspectj.lang.annotation.Aspect;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component; import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; @Aspect
@Component
public class RedisRequestLockInterceptor extends AbstractRequestLockInterceptor {
@Resource
private Redisson redisson; @Override
protected Lock getLock(String key) {
return redisson.getLock(key);
} @Override
protected boolean tryLock(long waitTime, long leaseTime, TimeUnit unit, Lock lock) throws InterruptedException {
return ((RLock) lock).tryLock(waitTime, leaseTime, unit);
}
}
ProductService.java
/**
* 注意:key={"#proKey","#proId"} 与参数名一致时走Spell表达式
* @param proKey
* @param proId
*/
@RequestLockable(key={"#{#proKey}","#{#proId}"}) //细化到参数,参数值不同时,不共享一个锁,相同时才会共享,这点要注意
//@RequestLockable(key={"#lock_key_prod"})
public void anotationProd(String proKey,int proId){
String productId = stringRedisTemplate.opsForValue().get(proKey);
int sprodId = Integer.parseInt(productId);
if (sprodId > 0) {
stringRedisTemplate.opsForValue().set("product", --sprodId + "");
System.out.println(">>>>>>"+Thread.currentThread().getName() + ",product:" + sprodId + "");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
单元测试(比较懒,就不另外起工程测了,开多线程...)
@Test
public void testAnotationProd(){
//开启2个线程
Thread thread1 = new Thread(()-> productService.anotationProd("product",1));
Thread thread2 = new Thread(()-> productService.anotationProd("product",1)); thread1.start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start(); try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
场景1:方法加注释:@RequestLockable(key={"#{#proKey}","#{#proId}"})
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.1
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.1
>>>>>>Thread-10,product:92
>>>>>>Thread-9,product:91
场景2:方法加注释:@RequestLockable(key={"#lock_key_prod"})
RequestLockable:keys=#lock_key_prod
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#lock_key_prod
RequestLockable:keys=#lock_key_prod
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#lock_key_prod
>>>>>>Thread-9,product:90
>>>>>>Thread-10,product:89
场景3:方法不加注释,此时两个线程同时去读写了
>>>>>>Thread-9,product:88
>>>>>>Thread-10,product:88
场景4:测试另一个线程,参数proId=2,发现两个线程同时去读写了
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.1
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.2
>>>>>>Thread-9,product:87
>>>>>>Thread-10,product:87
参考:
https://blog.csdn.net/qq_15427331/article/details/54630999
Redisson实现分布式锁(二)的更多相关文章
- Redisson实现分布式锁(3)—项目落地实现
Redisson实现分布式锁(3)-项目落地实现 有关Redisson实现分布式锁前面写了两篇博客作为该项目落地的铺垫. 1.Redisson实现分布式锁(1)---原理 2.Redisson实现分布 ...
- Redisson实现分布式锁(2)—RedissonLock
Redisson实现分布式锁(2)-RedissonLock 有关Redisson实现分布式锁上一篇博客讲了分布式的锁原理:Redisson实现分布式锁---原理 这篇主要讲RedissonLock和 ...
- Redisson实现分布式锁(1)---原理
Redisson实现分布式锁(1)---原理 有关Redisson作为实现分布式锁,总的分3大模块来讲. 1.Redisson实现分布式锁原理 2.Redisson实现分布式锁的源码解析 3.Redi ...
- spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)
一,为什么要使用分布式锁? 如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性 但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁, ...
- 基于Redisson实现分布式锁源码解读
文章目录 一.分布式锁的概念 和 使用场景 二.将redis官网对于分布式锁(红锁)的定义和Redisson实现做概括性总结 三.基于Redisson的分布式实现方案 四.加锁过程分析 五.锁重入过程 ...
- redisson之分布式锁实现原理(三)
官网:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 一.什么是分布式锁 1.1.什么是分布式锁 分布式锁,即分布式系统中的锁 ...
- Redisson实现分布式锁
转: Redisson实现分布式锁 Redisson文档参考:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 redis是实现 ...
- 使用Redisson实现分布式锁,Spring AOP简化之
源码 Redisson概述 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).它不仅提供了一系列的分布式的Java常用对象,还提供了许多 ...
- 利用Redisson实现分布式锁及其底层原理解析
Redis介绍 参考地址:https://blog.csdn.net/turbo_zone/article/details/83422215 redis是一个key-value存储系统.和Memcac ...
- 【高并发】你知道吗?大家都在使用Redisson实现分布式锁了!!
写在前面 忘记之前在哪个群里有朋友在问:有出分布式锁的文章吗-@冰河?我的回答是:这周会有,也是[高并发]专题的.想了想,还是先发一个如何使用Redisson实现分布式锁的文章吧?为啥?因为使用Red ...
随机推荐
- ARM 架构、ARM7、ARM9、STM32、Cortex M3 M4 、51、AVR 之间有什么区别和联系?(转载自知乎)
ARM架构: 由英国ARM公司设计的一系列32位的RISC微处理器架构总称,现有ARMv1~ARMv8种类. ARM7: 一类采用ARMv3或ARMv4架构的,使用冯诺依曼结构的内核. ...
- yolo3(目标检测)实测
yolo是继faster-r-cnn后,原作者在目标检测领域进行的新研究.到了v3版本以后,虽然已经换人支持,但是更注重工程实践,在实际使用过程中突出感受就是 “非常快”,GPU加速以后能够达到实时多 ...
- 20145308 《网络对抗》 MSF基础应用 学习总结
20145308 <网络对抗> MSF基础应用 学习总结 实验内容 掌握metasploit的基本应用方式,掌握常用的三种攻击方式的思路.具体需要完成(1)一个主动攻击,如ms08_067 ...
- Codeforces 903G Yet Another Maxflow Problem - 线段树
题目传送门 传送门I 传送门II 传送门III 题目大意 给定一个网络.网络分为$A$,$B$两个部分,每边各有$n$个点.对于$A_{i} \ (1\leqslant i < n)$会向$A_ ...
- Codeforces 40E Number Table - 组合数学
题目传送门 传送门I 传送门II 题目大意 给定一个$n\times m$的网格,每个格子上要么填$1$,要么填$-1$,有$k$个位置上的数是已经填好的,其他位置都是空的.问有多少种填法使得任意一行 ...
- topcoder srm 445 div1
problem1 link 这个的结论是只需要考虑坐标是整数或者是整数.5,比如(2.5,3),(4,3.5),(1.5,4.5)这样的时候.这个详细证明起来应该挺麻烦的.这里有一些讨论. probl ...
- IPVS负载均衡
概念: ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4层LAN交换,作为 Linux 内核的一部分.ipvs运行在主机上,在真实服务器集群前充当负载均衡器. ...
- HihoCoder 1634 Puzzle Game(最大子矩阵和)题解
题意:给一个n*m的矩阵,你只能选择一个格子把这个格子的数换成p(也可以一个都不换),问最大子矩阵和最小可能是多少? 思路: 思路就是上面这个思路,这里简单讲一下怎么n^3求最大子矩阵和:枚举两行(或 ...
- .Net Core 全球化&本地化的使用
官网文档 nuget地址 创建资源文件 添加资源文件 实施策略 配置本地化 本地化中间件 使用 视图本地化 DataAnnotations 本地化 Make the app's content loc ...
- POJ 3278 Catch That Cow(赶牛行动)
POJ 3278 Catch That Cow(赶牛行动) Time Limit: 1000MS Memory Limit: 65536K Description - 题目描述 Farmer J ...