文章目录
Redis-习-02-Redis分布式锁(Redisson)
自己写的基于Redis+Lua脚本的分布式锁,经测试存在一些Redis连接池方面的问题,同时存在多节点Redis情况下的使用不严谨,故采用官方推荐的Redisson框架来实现。
- Redisson官方文档
- 项目使用框架为SpringBoot,Redisson提供了整合SpringBoot的jar包,在这里并没有使用,因为如果使用了这个整合包,就会整体替换掉项目中Redis的客户端类型。项目中之前使用的是Spring自带的RedisTemplate,也就是Jedis客户端。
1. 添加maven依赖
- 这是独立的Redisson的maven依赖包,而不是Redisson-SpringBoot整合包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.10.1</version>
</dependency>
- 这是Redisson官方提供的整合SpringBoot的包(我没有用这种方式)
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-16</artifactId>
<version>3.11.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.11.3</version>
</dependency>
2. 编写配置RedissonClient的配置类
Redisson官方文档,官方文档中对配置有非常详细的说明,对于Redis是单节点、集群、哨兵、云服务厂商等都有不同的配置方式
- 编写配置类,实例化RedissonClient。我这里用的读取yml配置文件的方式,官方还有读取json文件方式或者代码中配置
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.TransportMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() throws IOException {
// 本例子使用的是yaml格式的配置文件,读取使用Config.fromYAML,如果是Json文件,则使用Config.fromJSON
Config config = Config.fromYAML(RedissonConfig.class.getClassLoader().getResource("redisson.yml"));
return Redisson.create(config);
}
}
- 编写 redisson.yml(放在项目的resource目录下)。Redis单节点、集群、哨兵模式的配置内容不一样,具体看官方文档。我这里用的是哨兵模式配置,是复制的官方提供的模板
sentinelServersConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
password: null
subscriptionsPerConnection: 5
clientName: null
loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
slaveSubscriptionConnectionMinimumIdleSize: 1
slaveSubscriptionConnectionPoolSize: 50
slaveConnectionMinimumIdleSize: 32
slaveConnectionPoolSize: 64
masterConnectionMinimumIdleSize: 32
masterConnectionPoolSize: 64
readMode: "SLAVE"
sentinelAddresses:
- "redis://127.0.0.1:26400"
- "redis://127.0.0.1:26401"
- "redis://127.0.0.1:26402"
masterName: "mymaster"
database: 0
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: "NIO"
- 这是代码配置的举例,里面共写了三种,只需要其中一种即可(个人不喜欢这种方式,毕竟是写死在代码中,还是使用配置文件的方式好,因为以后有模式的修改等等都只要改配置文件,不用修改代码)
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
// 哨兵模式
config.useSentinelServers()
.setMasterName("mymaster")
.addSentinelAddress("redis://127.0.0.1:26400","redis://127.0.0.1:26401","redis://127.0.0.1:26402");
// 单节点模式
config.setTransportMode(TransportMode.NIO);
config.useSingleServer().setAddress("redis://127.0.0.1:6400");
// cluster 集群模式
config.useClusterServers()
.setScanInterval(2000)
.addNodeAddress("redis://127.0.0.1:7001", "redis://127.0.0.1:7002")
.addNodeAddress("redis://127.0.0.1:7003", "redis://127.0.0.1:7004")
.addNodeAddress("redis://127.0.0.1:7005", "redis://127.0.0.1:7006");*//*
return Redisson.create(config);
}
3. 代码中使用 RedissonClient Bean实例
- Redisson官方文档,这里面有使用锁的详细用法
- 我这里简单举例使用
@Autowired
private RedissonClient redissonClient;
@RequestMapping("/testLock")
public R testLock(){
RLock lock = redissonClient.getLock("testLock");
boolean tryLock = false;
try {
// 第一个参数是获取锁最长等待时间(超时时间),第二个是锁的有效时间(缓存到Redis时间),第三个为时间类型
// 示例:获取锁最长等待3秒,3秒内没获取到,tryLick 为 false。缓存设置为2秒
tryLock = lock.tryLock(3, 2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (tryLock) {
lock.unlock();
}
}
return R.ok();
}
- 红锁 RedLock 使用
参考
关于 Redis 官方推荐的 RedLock 算法使用,这里有点模糊的是关于节点的数量问题。比如我本地用的一套哨兵集群,内有三个Redis节点,一主两从。事实上这个在 RedLock 中只算作一个节点。RedLock中节点的意思是相互独立的,不存在数据共享或者复制情况。如三套哨兵集群,三套Cluster集群。三个独立节点
参考说明:核心的变化就是需要构建多个 RLock ,然后根据多个 RLock 构建成一个 RedissonRedLock,因为 redLock 算法是建立在多个互相独立的 Redis 环境之上的(为了区分可以叫为 Redission node),Redission node 节点既可以是单机模式(single),也可以是主从模式(master/salve),哨兵模式(sentinal),或者集群模式(cluster)。这就意味着,不能跟以往这样只搭建 1个 cluster、或 1个 sentinel 集群,或是1套主从架构就了事了,需要为 RedissonRedLock 额外搭建多几套独立的 Redission 节点。 比如可以搭建3个 或者5个 Redission节点,具体可看视资源及业务情况而定。
@RequestMapping("/redLock")
public R redLock() throws InterruptedException {
RLock lock1 = redissonClient.getLock("redLock");
RLock lock2 = redissonClient.getLock("redLock");
RLock lock3 = redissonClient.getLock("redLock");
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
boolean flag = false;
try {
// 第一个参数是获取锁最长等待时间(超时时间),第二个是锁的有效时间(缓存到Redis时间),第三个为时间类型
// 示例:获取锁最长等待3秒,3秒内没获取到,tryLick 为 false。缓存设置为2秒
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
flag = redLock.tryLock(3, 2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (flag) {
redLock.unlock();
}
}
return R.ok().put("data", flag);
}