一、示例一:使用AOP实现Redis分布式锁
1、在需要添加同步控制的业务对象中,首先引入redissonClient;
2、在具体业务方法上创建锁对象 redissonClient.getLock;
(getLock中的参数是自己根据业务拼接的字符串,整个系统中如果遇到相同字符串的锁定变量信息,将同步处理)
3、对同步块采用封装处理;
注:getLock与tryLock区别:
RLock lock=redissonClient.getLock():创建锁对象;
lock.tryLock():尝试获取锁;
public class TestReentrantLock {
@Autowired
private RedissonClient redissonClient;
RLock lock = redissonClient.getLock(${锁定变量});
try{
if (lock.tryLock(10, TimeUnit.SECONDS)) {//尝试获取锁,如果超过10秒,进入异常流程
try {
//获得锁,进行业务处理(处理过程中同样的SceneType.VALIDATE_MOBILE.name()+uuid 值在全局内不能再次进入,保证同步性)
//业务代码块
} catch (Exception e) {
//业务报错异常处理分支
} finally {
lock.unlock();// 释放锁
}
} else {
//获取锁超时处理分支
//可以返回可控的处理信息 例如:"系统繁忙,请过后尝试!"
}
} catch(InterruptedException e){
// trylock 线程阻塞Interrupt
// 静默处理!
}
}
二、示例二:使用AOP实现Redis分布式锁
第一步:自定义切面类:
@Component
@Aspect
@Slf4j
public class RedisLockAspect {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisProperties redisProperties;
@Around("@annotation(com.asd.core.annotation.RedisLock)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
Method proxyMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();
Method realMethod = joinPoint.getTarget().getClass().getDeclaredMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
RedisLock redisLock = realMethod.getAnnotation(RedisLock.class);
if (joinPoint.getArgs().length>0&&joinPoint.getArgs()[0] instanceof UserDTO){
String certNo=((UserDTO)joinPoint.getArgs()[0]).getCertNo();
//Redis分布式锁,同一用户同一时间只能单一查询
RLock lock=null;
try{
lock = redissonClient.getLock(redisLock.prefix()+realMethod.getName()+":"+certNo);
log.debug("lock:{}",lock.getName());
if (!lock.tryLock(-1, redisProperties.getLockExpireInSeconds(), TimeUnit.SECONDS)){
throw new SametimeQueryException();
}
}catch (RedisException e){
log.error("Redis异常,加锁失败",e);
}
try {
return joinPoint.proceed();
}finally {
try{
if (lock!=null){
lock.unlock();
}
}catch (RedisException e){
log.error("Redis异常,解锁失败",e);
}
}
}
//一般不会走到这步,除非方法参数异常
throw new UnknownException();
}
}
第二步:自定义切面注解:
/**
* Redis分布式锁
* (该注解用于方法上可以实现分布式同步锁)
*/
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
String prefix() default "shop:Lock:";
long waitSecond() default -1;
long expireTime() default 10;
TimeUnit unit() default TimeUnit.SECONDS;
}
第三步:业务类中使用切面注解:
查询时:同一用户同一时间只能单一查询;
public class QueryServiceImpl implements QueryService{
@RedisLock
@Override
public User queryUser(String name){
...
}
}
注意一: tryLock()方法介绍:
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待;具有三种构造方法:①无参;②包含 失效时间、单位 两个参数的构造方法;③包含 等待时间,失效时间,单位三个参数的构造方法; 示例二的自定义切面类中用的是第三种。
public class TestReentrantLock {
public void testReentrantLock(RedissonClient redisson){
RLock lock = redisson.getLock("anyLock");
try{
//1.无参 (最常见的使用方法)
//lock.lock();
//2.两个参数:失效时间,单位 (支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁)
//lock.lock(10, TimeUnit.SECONDS);
//3.三个参数:等待时间,失效时间,单位 (尝试加锁,最多等待3秒,上锁以后10秒自动解锁)
boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
if(res){ //成功
// do your business
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
注意二:1. RedisProperties.java:
@Component
@Data
@ConfigurationProperties("asd.core.redis")
public class RedisProperties {
private Integer lockExpireInSeconds;
private Integer rateLimiterTimeout;
}
2. application.yml:
pboc:
core:
redis:
lock-expire-in-seconds: 30
rate-limiter-timeout: 10
3. 对于@ConfigurationProperties(“asd.core.redis”)的使用:
在SpringBoot使用@ConfigurationProperties注解读取yml/properties配置文件参数:在配置文件中所配置的参数都正确的注入到Java类对象中,而类中存在,但配置文件中没有配置的属性值默认为null。