以前在开发项目的时候,一般使用自己基于 lua
脚本实现的 redis
锁,最近对分布式所深入了解了一下,才发现这样实现是有很大的问题的,比如说,如果线程执行时间过长,在锁释放之后还没有执行完成怎么办呢
最近又发现了一个比较好的 redis
的框架 redisson
这里面实现了 Watch Dog
自动延期功能,在这里记录一下,以便以后查阅
一、添加 redisson 配置
引入依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>x.x.x</version>
</dependency>
添加配置(这里我就直接复制我的配置了):
@Configuration
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
// 是否启动集群模式
public boolean clusterEnabled = false;
// redis 服务器地址,多个以逗号分割
private String address;
// 使用的redis数据库编号
private int database = 0;
// redis 登录密码
private String password;
// 服务器响应超时时间
private int timeout = 3000;
// redis 集群扫描间隔
private int scanInterval = 1000;
// redis连接池大小,若为集群,则为主节点连接池大小
private int connectionPoolSize = 64;
// 每个从节点连接连接池大小
private int slaveConnectionPoolSize = 250;
// redis 最小空闲连接
private int connectionMinimumIdleSize = 10;
// ... get and set
}
@Bean
public RedissonClient redissonClient(RedissonProperties redissonProperties) {
Config config = new Config();
if(redissonProperties.clusterEnabled) {
ClusterServersConfig serverConfig = config.useClusterServers()
.addNodeAddress(redissonProperties.getAddress().split(","))
.setTimeout(redissonProperties.getTimeout())
.setScanInterval(redissonProperties.getScanInterval())
.setMasterConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize())
.setMasterConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize())
.setSlaveConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
if(!StringUtils.isEmpty(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
} else {
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redissonProperties.getAddress())
.setDatabase(redissonProperties.getDatabase())
.setTimeout(redissonProperties.getTimeout())
.setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
if(!StringUtils.isEmpty(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
}
config.setCodec(new JsonJacksonCodec());
return Redisson.create(config);
}
二、相关锁方法介绍
1. 获取Lock实例
// 非公平锁,无法实现线程顺序获取锁
RLock lock = redissonClient.getLock("lock-key");
// 公平锁,实现线程顺序获取锁
RLock lock = redissonClient.getFairLock("lock-key");
// 红锁,获取多个关联锁的红锁实例
RLock lock = redissonClient.getRedLock();
// 多锁,获取多个关联锁的实例
RLock lock = redissonClient.getMultiLock();
// 读写锁,不做讲解
ReadWriteLock readWriteLock = redissonClient.getReadWriteLock("");
2. RLock方法详解
// 具有Watch Dog自动延期机制; 锁默认存在时间为30s;每隔30/3=10s自动续期到30s
// 尝试获取锁,获取失败时,会不停的重试;直至拿到锁
lock.lock();
// 没有Watch Dog自动延期机制;在租赁到期后,释放锁
// 尝试获取锁,获取失败时,会不停的重试;直至拿到锁
lock.lock(120000, TimeUnit.SECONDS);
// 具有Watch Dog自动延期机制; 锁默认存在时间为30s;每隔30/3=10s自动续期到30s
// 获取锁失败时,会等待,直至达到最大等待时间,返回false
boolean bool = lock.tryLock(5, TimeUnit.SECONDS);
// 没有Watch Dog自动延期机制;在租赁到期后,释放锁
// 获取锁失败时,会等待,直至达到最大等待时间,返回false
boolean bool = lock.tryLock(5, 60, TimeUnit.SECONDS);
3. Wath Dog 自动延期机制
加入一个线程拿到了锁,设置30s超时,但是在30s后这个线程还没有执行完毕,锁释放了;这就会导致问题;Redisson
的 Watch Dog
自动延期机制,正好可以解决这个问题。
Redisson
提供了一个监控锁的 看门狗
(Wath Dog);做事在 Redisson
实例被关闭前,不断延长锁的有效期;也就是说,一个线程拿到锁,一直没有执行完逻辑,那么 看门狗
会不断延长锁超时时间。
默认情况下,看门狗
续期超时时间是30s,我们可以通过 Config.lockWatchdogTimeout
来设置这个时间,另外 Redisson
加锁时,也提供了 leaseTime
参数设置锁租赁时间。超过这个时间,锁就会自动解开,不会触发延期。