redis可以实现分布式锁,redisson就是对redis分布式锁的一个成功实现成品,当然我们自己也可以用redis实现分布式锁,不过既然有轮子了,为啥还要造呢,哈哈哈,这里重属笔记。
捋一下redis实现分布式的一些理念信的东西吧
redis可以用setnx命令实现分布式锁,然后在设置过期时间(避免死锁,执行完业务finall里释放锁),不过由于这两条命令并不是原子操作,所以高并发下还会出现问题,还好redis高版本提供了set(key,1,30,NX)命令,相当于setnx命令和设置过期时间,这是一个原子操作,这种方式,如果30秒内任务没有执行完成,那么redis锁会过期,其他线程发现没锁也会进入代码,那么就会出现第一个线程执行完会把第二个线程上的锁给释放掉,这种情况处理方式:将线程id当做value,key为锁,释放锁时,从redis中get到锁,判断锁是否为当前线程所上,但是就算这样也会有多个线程执行同一段代码,(锁永久失效),还有处理方法是,守护线程延续时间。。。。。自己使用redis实现完美分布式锁还是蛮麻烦的,所以就用redisson吧,接下来就直接上代码吧
pom中添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.9.1</version>
</dependency>
配置RedissonClient 的bean,下面代码,根据不同配置文件,创建不同bean,
单机版,或者集群版
package com.annaru.common.config.redis;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
@Configuration
public class RedissonConfig {
@Value("${annaru.redisson.model}")
private String redissonModel;
/**
* redisson客户端
*
* @return
* @throws IOException
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() throws IOException {
Config config = null;
if ("single".equals(redissonModel)) {
config = Config.fromYAML(new ClassPathResource("redisson-single-config.yml").getInputStream());
} else if ("cluster".equals(redissonModel)) {
config = Config.fromYAML(new ClassPathResource("redisson-cluster-config.yml").getInputStream());
}
return Redisson.create(config);
}
}
接口层
package com.annaru.common.config.redis;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* @Description redisson 分布式锁接口
**/
public interface RedissonLocker {
RLock lock(String lockKey);
/**
* @param lockKey
* @param leaseTime 加锁时间,单位秒
* @return
*/
RLock lock(String lockKey, Long leaseTime);
/**
* @param lockKey
* @param unit
* @param timeout 加锁时间
* @return
*/
RLock lock(String lockKey, TimeUnit unit, Long timeout);
/**
* 测试锁,拿到lock就返回true,否则返回false
* 时间限制,拿不到lock,就等一段时间,超时返回false
*
* @param lockKey
* @param unit
* @param waitTime
* @param leaseTime
* @return
*/
boolean tryLock(String lockKey, TimeUnit unit, Long waitTime, Long leaseTime);
/**
* 解锁
*
* @param lockKey
*/
void unlock(String lockKey);
/**
* 解锁
*
* @param lock
*/
void unlock(RLock lock);
}
实现类
业务代码中使用的话,只需要调用此类方法即可,
package com.annaru.common.config.redis;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @Description redisson 分布式锁接口实现类
**/
@Component
public class RedissonLockerImpl implements RedissonLocker {
@Autowired
private RedissonClient redissonClient;
/**
* 拿不到锁就不罢休,线程就一直lock
*
* @param lockKey
* @return
*/
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
@Override
public RLock lock(String lockKey, Long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}
@Override
public RLock lock(String lockKey, TimeUnit unit, Long timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
@Override
public boolean tryLock(String lockKey, TimeUnit unit, Long waitTime, Long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.lock();
}
}
配置文件
##Redisson 单节点模式配置
singleServerConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
password: null
subscriptionsPerConnection: 5
clientName: null
address: "redis://115.159.9*.*75:6379"
# address: "redis://localhost:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 32
connectionPoolSize: 64
database: 0
#dnsMonitoring: false
dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: "NIO"