基于springBoot的redisson分布式锁
之前使用Redis分布式锁都是自己写的工具类,利用Redis的setNX特性;后来发现Redisson提供的分布式锁是真的好用。
Redisson可以看做是对Redis的一个操作工具类。将原生的RedisHash,List,Set,String等数据结构封装为Java里大家最熟悉的映射(Map),列表(List),集(Set)等。
我的项目中使用了RedisTemplate,现在要在此基础上使用Redisson,如果再在配置文件里面添加一份Redisson的配置,那维护起来就非常麻烦,如果改动了配置信息,就要改动两个地方的配置。所以写一个RedissonConfig配置类,在配置里面读取RedisTemplate的配置信息即可;
一、配置文件
1.pom文件添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
2.yml配置文件配置
spring
redis:
database: 0
host: 1.1.1.1
port: 6379
password: 123456
cluster:
nodes: 1.1.1.1:6379, 1.1.1.1:6379, 1.1.1.1:6379, 1.1.1.1:6379, 1.1.1.1:6379
二、RedisTemplate序列化配置
// 配置序列化
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
redisConnectionFactory.setDatabase(1);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
三、RedissonConfig配置
@ConfigurationProperties(prefix = "spring.redis")
@Component
@Data
public class RedissonConfig {
// 读取配置文件里面的Redis信息
private String password;
private Cluster cluster;
public static class Cluster {
private List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
/**
* 配置redisson
* @return
*/
@Bean
public Redisson redisson() {
List<String> clusterNodes = new ArrayList<>();
for (int i = 0; i < this.getCluster().getNodes().size(); i++) {
clusterNodes.add("redis://" + this.getCluster().getNodes().get(i));
}
Config config = new Config();
ClusterServersConfig clusterServersConfig = config.useClusterServers()
.addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));
clusterServersConfig.setPassword(getPassword());
return (Redisson) Redisson.create(config);
}
}
四、编写RedissonUtil工具类
@Component
public class RedissonUtil {
private static final Logger logger = LoggerFactory.getLogger(RedissonUtil.class);
/**
* redis锁前缀
*/
public static final String SYS_LOCK_FLAG = "MY_LOCK";
/**
* 用于隔开缓存前缀与缓存键值
*/
public static final String KEY_SPLIT = ":";
// 静态属性注入
private static Redisson redisson;
@Autowired
public void setRedisson(Redisson redisson) {
RedissonUtil.redisson = redisson;
}
/**
* 加锁,一直等待直到获得锁为止,不建议使用
*
* @param lockName 锁名 相同的key表示相同的锁,建议针对不同的业务使用不同的key
* @param expiresTime 过期时间,单位:秒
* @return
*/
public static boolean getLock(String lockName, long expiresTime) {
String key = getLockKey(lockName);
//获取锁对象
RLock lock = redisson.getLock(key);
//设置锁过期时间,防止死锁的产生
lock.lock(expiresTime, TimeUnit.SECONDS);
logger.info("获取锁成功,Redis Lock key :{}", key);
return true;
}
/**
* 加锁,规定时间内没抢到锁就放弃
*
* @param lockName 锁名 相同的key表示相同的锁,建议针对不同的业务使用不同的key
* @param waitTime 最大等待锁时间
* @param expiresTime 锁过期时间,单位:秒
* @return
* @throws InterruptedException
*/
public static boolean getTryLock(String lockName, long waitTime, long expiresTime) {
String key = getLockKey(lockName);
//获取锁对象
RLock lock = redisson.getLock(key);
//设置锁过期时间,防止死锁的产生
boolean lockFlag = false;
try {
lockFlag = lock.tryLock(waitTime, expiresTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.error("加锁出现异常", e);
} finally {
if (lockFlag) {
unlock(lockName);
}
}
String msg = "获取锁失败";
if (lockFlag) {
msg = "获取锁成功";
}
logger.info("{},Redis Lock key :{}", msg, key);
return lockFlag;
}
/**
* 释放锁,建议放在 finally里面
*
* @param lockName 锁名称
*/
public static void unlock(String lockName) {
String key = getLockKey(lockName);
//获取所对象
RLock lock = redisson.getLock(key);
// 释放锁,判断要解锁的key是否已被锁定并且是否被当前线程保持
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
logger.info("释放Redis锁成功,key:{}", key);
}
}
/**
* 对锁的key添加系统标识前缀
*
* @return
*/
private static String getLockKey(String key) {
return RedissonUtil.SYS_LOCK_FLAG + RedissonUtil.KEY_SPLIT + key;
}
}
五、使用
@Test
public void redissonLock(){
String key = "zhh11";
long waitTime = 10;
long expiresTime = 10;
// 加锁
if (RedissonUtil.getTryLock(key, waitTime, expiresTime)){
try{
Thread.sleep(3000L);
// 业务代码
}catch (Exception e){
e.printStackTrace();
}finally {
// 释放锁
RedissonUtil.unlock(key);
// 捕获异常之后需要 手动回滚事务
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}else {
System.out.println("未获取到锁");
}
}