之前业务里面实现分布式锁,是通过redis的计数器来实现的,那种方式当服务宕机时,key会永久保存在redis里面,
所以切到 redisson上,redisson获取锁时解决了死锁问题,底层通过调用lua脚本来实现写入key和设置过期时间的原子性.
更多redisson 的信息可以参考官方文档:
https://yq.aliyun.com/articles/554765?spm=a2c4e.11155435.0.0.634056a3ail7EF
jdk 1.6+, redis 2.8+
org.redisson
redisson
3.5.4
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
/**
* redis 锁服务
*
* @author Demon-HY
* @date 2019-8-19
*/
@Service
public class RedissonClientService {
@Value("${redis.host}")
private String redisHost;
@Value("${redis.port}")
private String redisPort;
@Value("${redis.auth}")
private String redisAuth;
@Value("${redis.timeout}")
private Integer redisTimeout;
private RedissonClient redissonClient;
@PostConstruct
public void afterCreation() {
Config config = new Config();
// 单实例模式
config.useSingleServer()
.setAddress(String.format("redis://%s:%s", redisHost, redisPort))
.setPassword(redisAuth)
// 同任何节点建立连接时的等待超时,时间单位是毫秒,默认:10000
.setConnectTimeout(30000)
// 当与某个节点的连接断开时,等待与其重新建立连接的时间间隔,时间单位是毫秒,默认:3000
.setReconnectionTimeout(10000)
// 等待节点回复命令的时间,该时间从命令发送成功时开始计时,默认:3000
.setTimeout(10000)
// 如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误.
// 如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时,默认值:3
.setRetryAttempts(5)
// 在一条命令发送失败以后,等待重试发送的时间间隔,时间单位是毫秒,默认值:1500
.setRetryInterval(3000);
redissonClient = Redisson.create(config);
}
/**
* 获取锁
*
* @param lockName 锁名称
* @return
*/
public RLock getLock(String lockName) {
return redissonClient.getLock(lockName);
}
/**
* 根据name对进行上锁操作
*
* @param lockName 锁名称
* @param timeout 超时时间,到期后强制解锁,防止死锁,单位:秒
* @return
*/
public void lock(String lockName, long timeout) {
RLock lock = getLock(lockName);
//lock提供带timeout参数,timeout结束强制解锁,防止死锁
lock.lock(timeout, TimeUnit.SECONDS);
}
/**
* 根据name对进行上锁操作,redisson tryLock 根据第一个参数,一定时间内为获取到锁,则不再等待直接返回boolean。交给上层处理
*
* @param lockName 锁名称
* @param waitTime 等待时间,到期后获取不到锁则直接返回,单位:秒
* @param timeout 获取锁超时时间,到期后强制释放锁,单位:秒
*/
public boolean tryLock(String lockName, long waitTime, long timeout) throws InterruptedException {
RLock lock = getLock(lockName);
//tryLock,第一个参数是等待时间,5秒内获取不到锁,则直接返回。 第二个参数 60是60秒后强制释放
return lock.tryLock(waitTime, timeout, TimeUnit.SECONDS);
}
/**
* 根据name对进行解锁操作
*
* @param lockName 锁名称
*/
public void unlock(String lockName) {
RLock lock = getLock(lockName);
lock.unlock();
}
}
@Autowired
private RedissonClientService redissonClientService;
public void testLock(String lockName) {
RLock lock = redissonClientService.getLock(lockName);
try {
//tryLock,第一个参数是等待时间,0秒内获取不到锁,则直接返回。 第二个参数 是180秒后强制释放
if (!lock.tryLock(0L, 180L, TimeUnit.SECONDS)) {//获取不到锁,直接返回
logger.warn("获取锁:{} 失败", lockName);
return;
}
// 获取到锁,执行业务代码
// ...
} catch (Exception e) {
logger.error("业务代码执行异常");
} finally {
lock.unlock();
}
}