用Redisson实现分布式锁
2019-12-08 本文已影响0人
HugasChan
学无止境,厚积薄发
一、pom.xml文件中引入redisson依赖包
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.2.13</version>
</dependency>
二、在resources/conf下创建redis.properties文件
redis.host=127.0.0.1
redis.port=6379
redis.password=123456
redis.database=3
redis.timeout=1800
redis.pool.maxTotal=1000
redis.pool.maxIdle=200
redis.pool.maxWaitMillis=1000
redis.pool.testOnBorrow=false
三、自定义分布式注解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 java.util.concurrent.TimeUnit;
/**
* 分布式锁的注解, 通过指定key作为分布式锁的key
*
* @author chenxj
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Documented
public @interface RedisLock {
/**
* 分布式锁的key
* @return
*/
String key();
/**
* 分布式锁用的业务场景id
* @return
*/
String biz() default "";
/**
* 过期时间, 默认是5秒
* 单位是秒
* @return
*/
int keepMills() default 120;
/**
* 没有获取到锁时,等待时间
* 单位是秒
* @return
*/
long maxSleepMills() default 120;
/**
* 单位默认秒
* @return
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
四、创建Redis client的辅助工具类
import org.redisson.Config;
import org.redisson.Redisson;
import org.redisson.RedissonClient;
import org.redisson.core.RAtomicLong;
import org.redisson.core.RBlockingQueue;
import org.redisson.core.RBucket;
import org.redisson.core.RCountDownLatch;
import org.redisson.core.RDeque;
import org.redisson.core.RList;
import org.redisson.core.RLock;
import org.redisson.core.RMap;
import org.redisson.core.RQueue;
import org.redisson.core.RSet;
import org.redisson.core.RSortedSet;
import org.redisson.core.RTopic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Redis client的辅助工具类
* 用于连接Redis服务器 创建不同的Redis Server对应的客户端对象
*
* @author chenxj
*/
public class RedisUtils {
private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);
private static RedisUtils redisUtils;
private RedisUtils() {
}
/**
* 提供单例模式
*
* @return
*/
public static RedisUtils getInstance() {
if (redisUtils == null)
synchronized (RedisUtils.class) {
if (redisUtils == null) redisUtils = new RedisUtils();
}
return redisUtils;
}
/**
* 使用config创建Redisson
* Redisson是用于连接Redis Server的基础类
*
* @param config
* @return
*/
public RedissonClient getRedisson(Config config) {
RedissonClient redisson = Redisson.create(config);
logger.info("成功连接Redis Server");
return redisson;
}
/**
* 使用ip地址和端口创建Redisson
*
* @param ip
* @param port
* @return
*/
public RedissonClient getRedisson(String ip, String port) {
Config config = new Config();
config.useSingleServer().setAddress(ip + ":" + port);
RedissonClient redisson = Redisson.create(config);
logger.info("成功连接Redis Server" + "\t" + "连接" + ip + ":" + port + "服务器");
return redisson;
}
/**
* 关闭Redisson客户端连接
*
* @param redisson
*/
public void closeRedisson(RedissonClient redisson) {
redisson.shutdown();
logger.info("成功关闭Redis Client连接");
}
/**
* 获取字符串对象
*
* @param redisson
* @param objectName
* @return
*/
public <T> RBucket<T> getRBucket(RedissonClient redisson, String objectName) {
RBucket<T> bucket = redisson.getBucket(objectName);
return bucket;
}
/**
* 获取Map对象
*
* @param redisson
* @param objectName
* @return
*/
public <K, V> RMap<K, V> getRMap(RedissonClient redisson, String objectName) {
RMap<K, V> map = redisson.getMap(objectName);
return map;
}
/**
* 获取有序集合
*
* @param redisson
* @param objectName
* @return
*/
public <V> RSortedSet<V> getRSortedSet(RedissonClient redisson, String objectName) {
RSortedSet<V> sortedSet = redisson.getSortedSet(objectName);
return sortedSet;
}
/**
* 获取集合
*
* @param redisson
* @param objectName
* @return
*/
public <V> RSet<V> getRSet(RedissonClient redisson, String objectName) {
RSet<V> rSet = redisson.getSet(objectName);
return rSet;
}
/**
* 获取列表
*
* @param redisson
* @param objectName
* @return
*/
public <V> RList<V> getRList(RedissonClient redisson, String objectName) {
RList<V> rList = redisson.getList(objectName);
return rList;
}
/**
* 获取队列
*
* @param redisson
* @param objectName
* @return
*/
public <V> RQueue<V> getRQueue(RedissonClient redisson, String objectName) {
RQueue<V> rQueue = redisson.getQueue(objectName);
return rQueue;
}
/**
* 获取双端队列
*
* @param redisson
* @param objectName
* @return
*/
public <V> RDeque<V> getRDeque(RedissonClient redisson, String objectName) {
RDeque<V> rDeque = redisson.getDeque(objectName);
return rDeque;
}
/**
* 此方法不可用在Redisson 1.2 中
* 在1.2.2版本中 可用
*
* @param redisson
* @param objectName
* @return
*/
public <V> RBlockingQueue<V> getRBlockingQueue(RedissonClient redisson, String objectName) {
RBlockingQueue rb = redisson.getBlockingQueue(objectName);
return rb;
}
/**
* 获取锁
*
* @param redisson
* @param objectName
* @return
*/
public RLock getRLock(RedissonClient redisson, String objectName) {
RLock rLock = redisson.getLock(objectName);
return rLock;
}
/**
* 获取原子数
*
* @param redisson
* @param objectName
* @return
*/
public RAtomicLong getRAtomicLong(RedissonClient redisson, String objectName) {
RAtomicLong rAtomicLong = redisson.getAtomicLong(objectName);
return rAtomicLong;
}
/**
* 获取记数锁
*
* @param redisson
* @param objectName
* @return
*/
public RCountDownLatch getRCountDownLatch(RedissonClient redisson, String objectName) {
RCountDownLatch rCountDownLatch = redisson.getCountDownLatch(objectName);
return rCountDownLatch;
}
/**
* 获取消息的Topic
*
* @param redisson
* @param objectName
* @return
*/
public <M> RTopic getRTopic(RedissonClient redisson, String objectName) {
RTopic rTopic = redisson.getTopic(objectName);
return rTopic;
}
}
五、创建分布式锁拦截器RedisLockInterceptor
import java.lang.reflect.Method;
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.Config;
import org.redisson.RedissonClient;
import org.redisson.SingleServerConfig;
import org.redisson.core.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import com.chanxj.common.annotation.RedisLock;
import com.chanxj.common.util.RedisUtils;
/**
* redis锁拦截器实现
*
* @author chenxj
*
*/
@Aspect
@Component
public class RedisLockInterceptor {
private static Logger logger = LoggerFactory.getLogger(RedisLockInterceptor.class);
/**
* redis地址
*/
@Value("${redis.host}:${redis.port}")
private String address;
/**
* redis密码
*/
@Value("${redis.password}")
private String password;
/**
* 数据库编号
*/
@Value("${redis.database}")
private Integer database;
/**
* 超时
*/
@Value("${redis.timeout}")
private Integer timeout;
@Bean
public RedissonClient getRedissonClient(){
//redisson配置
Config config = new Config();
SingleServerConfig singleSerververConfig = config.useSingleServer();
singleSerververConfig.setAddress(address);
singleSerververConfig.setPassword(password);
singleSerververConfig.setDatabase(database);
singleSerververConfig.setTimeout(timeout);
//redisson客户端
RedissonClient redissonClient = RedisUtils.getInstance().getRedisson(config);
return redissonClient;
}
@Autowired
private RedissonClient redissonClient;
@Pointcut("@annotation(com.chanxj.common.annotation.RedisLock)")
public void redisLockAspect() {
}
@Around("redisLockAspect()")
public Object lockAroundAction(ProceedingJoinPoint point) {
//获取锁
RLock lock = null;
Object object = null;
logger.info("开始获取锁!");
try {
RedisLock redisLock = getRedisLockInfo(point);
RedisUtils redisUtils = RedisUtils.getInstance();
//获取锁键值
String lockKey = getRedisKey(point, redisLock);
lock = redisUtils.getRLock(redissonClient, lockKey);
if(lock != null){
//第一个参数代表等待时间,第二是代表超过时间释放锁,第三个代表设置的时间制
Boolean status = lock.tryLock(redisLock.maxSleepMills(), redisLock.keepMills(), redisLock.timeUnit());
if(status){
object = point.proceed();
}
}
} catch (Throwable e) {
logger.error("Redis锁操作异常{}", e);
} finally {
if (lock != null) {
lock.unlock();
}
}
logger.info("结束获取锁!");
return object;
}
/**
* 获取注解信息
* @param point
* @return
*/
private RedisLock getRedisLockInfo(ProceedingJoinPoint point) {
try {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
return method.getAnnotation(RedisLock.class);
} catch (Exception e) {
logger.info(e.getMessage());
}
return null;
}
/**
* 获取redis key值
* @param redisLock
* @return
*/
private String getRedisKey(ProceedingJoinPoint point, RedisLock redisLock){
String keyEL = redisLock.key();
//1、创建解析器
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keyEL);
//2、设置解析上下文(有哪些占位符,以及每种占位符的值)
EvaluationContext context = new StandardEvaluationContext(); //参数
//3、真实参数名(运行时获取参数名:arg0 arg1 arg2)
Object[] args = point.getArgs();
MethodSignature methodSignature = (MethodSignature) point.getSignature();
String[] parameterNames = methodSignature.getParameterNames();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
//4、解析
String key = expression.getValue(context).toString();
return key;
}
}
六、测试
@RedisLock(key="'test_' + #id")
public HashMap<String, Object> test(Integer id) {
//TODO 做一些不可描述的事
}
欢迎关注我的公众号:Java软件编程(javarjbc)