省略…
Redisson
提供了一个监控锁的看门狗(watch dog
),它的作用是在Redisson
实例被关闭前,不断(默认每10s)的延长锁(redis
中的目标key)的有效期(默认续期到30s),也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断的延长锁的超时时间,锁不会因为超时而被释放。加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题;
默认情况下,看门狗的续期时间是30s,也可以通过修改Config.lockWatchdogTimeout
来另行指定。
另外Redisson
还提供了可以指定leaseTime
参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。
public interface RedissonClient {
/**
* 获取锁(默认的非公平锁)
*/
RLock getLock(String name);
/**
* 获取公平锁
*/
RLock getFairLock(String name);
/**
* 获取读写锁
*/
RReadWriteLock getReadWriteLock(String name);
}
public interface RLock extends Lock, RExpirable, RLockAsync {
void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException;
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
void lock(long leaseTime, TimeUnit unit);
boolean forceUnlock();
boolean isLocked();
boolean isHeldByCurrentThread();
int getHoldCount();
}
<dependency>
<groupId>org.redissongroupId>
<artifactId>redisson-spring-boot-starterartifactId>
<version>3.13.6version>
<exclusions>
<exclusion>
<groupId>org.redissongroupId>
<artifactId>redisson-spring-data-23artifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.redissongroupId>
<artifactId>redisson-spring-data-21artifactId>
<version>3.13.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
关于版本,直接去官网找下最新版即可,通过redisson-spring-data-xx
来适配Spring
的版本。上述就是适配spring2.x
版本的配置;
redisson-spring-boot-starter
这个包下只有2个类:RedissonProperties
和RedissonAutoConfiguration
。阅读源码发现RedissonAutoConfiguration
会自动注册RedissonConnectionFactory
和RedissonClient
。
redisson-spring-boot-starter
支持两种配置方式,第一种是完全兼容spring-boot-starter-data-redis
的配置(redis
和redisson
都在application.yml
中配置);第二种是使用redisson
自己的配置方式,即在application.yml
中只配置redis
,然后使用spring.redis.redisson.config=classpath:redisson.yml
来指定redisson
的配置文件名称,然后在redisson.yml
中配置redisson
。
application.yml 文件:
spring:
## Redis配置
redis:
password: xxxx1234
cluster:
nodes: 10.163.xx.111:6381,10.163.xx.111:6382,10.163.xx.112:6381,10.163.xx.112:6382,10.163.xx.113:6381,10.163.xx.113:6382
jedis:
pool:
#最大连接数
max-active: 8
#最大阻塞等待时间(负数表示没限制)
max-wait: -1
#最小空闲
min-idle: 0
#最大空闲
max-idle: 8
#连接超时时间
timeout: 10000
##Redisson配置
redisson:
enable: true
cluster-servers-config:
cluster-nodes: ${spring.redis.cluster.nodes}
load-balancer-mode: RADOM
password: ${spring.redis.password}
slave-connection-minimum-idle-size: 8
slave-connection-pool-size: 16
sslEnableEndpointIdentification: false
threads: 8
nettyThreads: 8
transportMode: NIO
application.yml 文件:
spring:
## Redis配置
redis:
password: xxxx1234
cluster:
nodes: 10.163.xx.111:6381,10.163.xx.111:6382,10.163.xx.112:6381,10.163.xx.112:6382,10.163.xx.113:6381,10.163.xx.113:6382
jedis:
pool:
#最大连接数
max-active: 8
#最大阻塞等待时间(负数表示没限制)
max-wait: -1
#最小空闲
min-idle: 0
#最大空闲
max-idle: 8
#连接超时时间
timeout: 10000
##Redisson配置
redisson:
config-file:
yaml: classpath:redisson.yml
redisson.yml
文件:
# 单节点配置
singleServerConfig:
# 连接空闲超时,单位:毫秒
idleConnectionTimeout: 10000
# 连接超时,单位:毫秒
connectTimeout: 10000
# 命令等待超时,单位:毫秒
timeout: 3000
# 命令失败重试次数,如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。
# 如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。
retryAttempts: 3
# 命令重试发送时间间隔,单位:毫秒
retryInterval: 1500
# 密码
password: redis.shbeta
# 单个连接最大订阅数量
subscriptionsPerConnection: 5
# 客户端名称
clientName: axin
# # 节点地址
address: redis://redis.shbeta@m-redis-k8s.shbeta.ke.com:36479
# 发布和订阅连接的最小空闲连接数
subscriptionConnectionMinimumIdleSize: 1
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
# 最小空闲连接数
connectionMinimumIdleSize: 32
# 连接池大小
connectionPoolSize: 64
# 数据库编号
database: 6
# DNS监测时间间隔,单位:毫秒
dnsMonitoringInterval: 5000
# 线程池数量,默认值: 当前处理核数量 * 2
#threads: 0
# Netty线程池数量,默认值: 当前处理核数量 * 2
#nettyThreads: 0
# 编码
codec: ! {}
# 传输模式
transportMode : "NIO"
配置项需要参照org.redisson.config
,如果你想配置集群模式的Redisson
,就点 Config
的成员变量ClusterServersConfig
去看下里边有哪些可配置项。
配置了redis
和redisson
的信息之后,就可以在项目中使用@Autowired
注入org.redisson.api.RedissonClient
(redisson
提供的默认实现类是org.redisson.Redisson
,且redisson-spring-boot-starter
会自动往IOC容器中注册org.redisson.Redisson
)。
Redis
操作注入RedisTemplate
进行使用。Redission
分布式锁可以引入RedissionClient
进行使用。@RestController
public class RedissonController {
@Autowired
private RedissonClient redissonClient;
@GetMapping(value = "/redisson/{key}")
public String redissonTest(@PathVariable("key") String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock();
Thread.sleep(10000);
} catch (Exception e) {
} finally {
lock.unlock();
}
return "已解锁";
}
}
封装一下对redissonClient
的使用。
package com.xx.core.redisson.lock;
/**
* Redisson分布式锁接口
*
* RLock的实现有可重入非公平锁(RedissonLock)、可重入公平锁(RedissonFairLock)、联锁(RedissonMultiLock)、 红锁(RedissonRedLock)、 读锁(RedissonReadLock)、 写锁(RedissonWriteLock)等
*/
public interface DistributedLocker {
RLock lock(String lockKey);
RLock lock(String lockKey, long timeout);
RLock lock(String lockKey, TimeUnit unit, long timeout);
boolean tryLock(String lockKey, TimeUnit unit, long leaseTime);
boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);
void unlock(String lockKey);
void unlock(RLock lock);
}
/**
* Redisson分布式锁实现-使用可重入非公平锁(RedissonLock)
*/
@Component
public class RedissonDistributedLocker implements DistributedLocker {
@Autowired
private RedissonClient redissonClient;
public RLock lock(String lockKey) {
RLock lock = this.redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
public RLock lock(String lockKey, long timeout) {
RLock lock = this.redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
public RLock lock(String lockKey, TimeUnit unit, long timeout) {
RLock lock = this.redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
public boolean tryLock(String lockKey, TimeUnit unit, long leaseTime) {
RLock lock = this.redissonClient.getLock(lockKey);
try {
return lock.tryLock(0L, leaseTime, unit);
} catch (InterruptedException var7) {
return false;
}
}
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) {
return false;
}
}
public void unlock(String lockKey){
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.isLocked()) {
lock.unlock();
}
} catch (IllegalMonitorStateException localIllegalMonitorStateException) {}
}
public void unlock(RLock lock) {
try {
if (lock.isLocked()) {
lock.unlock();
}
} catch (IllegalMonitorStateException var3) {}
}
}
DistributedLocker
接口新增tryLockAndRun()
方法
public interface DistributedLocker {
/**
* @param lockKey
* @param unit
* @param waitTime
* @param leaseTime
* @param supplier 获取锁后要执行的业务逻辑
* @param scene 业务逻辑的场景,用于打印日志
* @param
* @return
*/
<T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene);
}
RedissonDistributedLocker
实现tryLockAndRun()
方法
public class RedissonDistributedLocker implements DistributedLocker {
@Override
public <T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene) {
final long start = SystemClock.now();
// 获取分布式锁,最长等待时间:10秒,20秒后自动释放。注意锁与事务的顺序:获取分布式锁 -> 开启事务 -> 执行业务 -> 提交事务 -> 释放分布式锁!!!
final boolean tryLock = this.tryLock(lockKey, unit, waitTime, leaseTime);
final long end = SystemClock.now();
if (!tryLock) {
log.error("[{}]获取分布式锁失败,lockKey = {},耗时{}ms", scene, lockKey, end - start);
throw new RequestResultException(JsonCommonCodeEnum.E0004);
}
// 注意:一定是获取锁成功后,才进行try{}finally{释放锁}
try {
log.info("[{}]获取分布式锁成功,lockKey = {},耗时{}ms", scene, lockKey, end - start);
return supplier.get();
} finally {
this.unlock(lockKey);
}
}
}