以前在开发项目的时候,一般使用自己基于 lua
脚本实现的 redis
锁,最近对分布式所深入了解了一下,才发现这样实现是有很大的问题的,比如说,如果线程执行时间过长,在锁释放之后还没有执行完成怎么办呢
最近又发现了一个比较好的 redis
的框架 redisson
这里面实现了 Watch Dog
自动延期功能,在这里记录一下,以便以后查阅
引入依赖:
<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); }
// 非公平锁,无法实现线程顺序获取锁 RLock lock = redissonClient.getLock("lock-key"); // 公平锁,实现线程顺序获取锁 RLock lock = redissonClient.getFairLock("lock-key"); // 红锁,获取多个关联锁的红锁实例 RLock lock = redissonClient.getRedLock(); // 多锁,获取多个关联锁的实例 RLock lock = redissonClient.getMultiLock(); // 读写锁,不做讲解 ReadWriteLock readWriteLock = redissonClient.getReadWriteLock("");
// 具有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);
加入一个线程拿到了锁,设置30s超时,但是在30s后这个线程还没有执行完毕,锁释放了;这就会导致问题;Redisson
的 Watch Dog
自动延期机制,正好可以解决这个问题。
Redisson
提供了一个监控锁的 看门狗
(Wath Dog);做事在 Redisson
实例被关闭前,不断延长锁的有效期;也就是说,一个线程拿到锁,一直没有执行完逻辑,那么 看门狗
会不断延长锁超时时间。
默认情况下,看门狗
续期超时时间是30s,我们可以通过 Config.lockWatchdogTimeout
来设置这个时间,另外 Redisson
加锁时,也提供了 leaseTime
参数设置锁租赁时间。超过这个时间,锁就会自动解开,不会触发延期。