基于Springboot的Redission 分布式锁配置及使用(单机、集群、哨兵、主从)
【代码】基于Springboot的Redission 分布式锁配置及使用(单机、集群、哨兵、主从)
·
一、引入MAVEN依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.4</version>
</dependency>
二、application.yaml配置
redisson:
server:
# 单机:stand-alone
host: 101.43.201.38
port: 6379
database: 0
type: stand-alone
# 哨兵:sentinel ;主从: 或者 master-slave 集群:cluster
# master: mymaster
# nodes: 172.16.39.18:26376,172.16.39.18:26377,172.16.39.18:26378
# password: csp172@eb5g
# database: 0
# type: sentinel
# 集群:cluster
# nodes: 172.16.39.18:26376,172.16.39.18:26377,172.16.39.18:26378
# password: csp172@eb5g
# database: 0
# type: cluster
#获取锁最长等待时间,单位秒
waitTime: 10
#锁过期最长时间,单位秒
leaseTime: 10
添加配置类
package com.microservice.stock.config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author zzkk
* @desc redisson配置,
* 如需使用,
* 请将配置加入到spring容器 ,放开@Component 、 @ConfigurationProperties,
* 并在yaml中添加配置
* @date 2022/9/22 10:39
*/
@Component
@ConfigurationProperties(prefix = "redisson.server")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RedissonProperties {
/**
* redis主机地址
*/
private String host;
/**
* PORT
*/
private String port;
/**
* 连接类型,支持stand-alone-单机节点,sentinel-哨兵,cluster-集群,master-slave-主从
*/
private String type;
/**
* redis 连接密码
*/
private String password;
/**
* 选取那个数据库
*/
private String database;
/**
* 节点信息
*/
private String nodes;
/**
* 主节点
*/
private String master;
public RedissonProperties setPassword(String password) {
this.password = password;
return this;
}
public RedissonProperties setDatabase(String database) {
this.database = database;
return this;
}
}
RedissionClient
package com.microservice.stock.config;
import com.microservice.stock.utils.redisson.StandAlone;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zzkk
* @desc RedissionConfig
* @date 2022/9/22 10:27
*/
@Configuration
@RequiredArgsConstructor
public class RedissonConfig {
private final RedissonProperties redissonProperties;
@Bean
public RedissonClient getRedisson() {
return StandAlone.getInstance(redissonProperties);
}
}
单机
package com.microservice.stock.utils.redisson;
import com.microservice.stock.config.RedissonProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.util.StringUtils;
/**
* @author zzkk
* @desc 单机
* @date 2022/9/22 11:06
*/
public class StandAlone {
public static volatile RedissonClient standAloneClient = null;
public static synchronized RedissonClient getInstance(RedissonProperties redissonProperties) {
String type = "stand-alone";
if (!type.equalsIgnoreCase(redissonProperties.getType().trim())) {
return Sentinel.getInstance(redissonProperties);
}
//实例没创建,才会进入内部的 synchronized 代码块,提高性能,防止每次都加锁
if (standAloneClient == null) {
//可能第一个线程在synchronized 代码块还没创建完对象时,第二个线程已经到了这一步,所以里面还需要加上判断
synchronized (StandAlone.class) {
//也许有其他线程已经创建实例,所以再判断一次
if (standAloneClient == null) {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer()
.setAddress("redis://" + redissonProperties.getHost() + ":" + redissonProperties.getPort());
if (StringUtils.hasText(redissonProperties.getPassword())) {
singleServerConfig.setPassword(redissonProperties.getPassword());
}
if (StringUtils.hasText(redissonProperties.getDatabase())) {
singleServerConfig.setDatabase(Integer.parseInt(redissonProperties.getDatabase()));
}
standAloneClient = Redisson.create(config);
}
}
}
return standAloneClient;
}
}
主从
package com.microservice.stock.utils.redisson;
import com.microservice.stock.config.RedissonProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.springframework.util.StringUtils;
import java.util.HashSet;
import java.util.Set;
/**
* @author zzkk
* @desc 主从
* @date 2022/9/22 14:33
*/
public class MasterSlave {
public static volatile RedissonClient masterSlaveClient = null;
public static synchronized RedissonClient getInstance(RedissonProperties redissonProperties) {
String type = "master-slave";
if (!type.equalsIgnoreCase(redissonProperties.getType())) {
return Cluster.getInstance(redissonProperties);
}
if (masterSlaveClient == null) {
synchronized (MasterSlave.class) {
if (masterSlaveClient == null) {
Config config = new Config();
String[] adds = redissonProperties.getNodes().split(",");
Set<String> nodeAddress = new HashSet<>();
for (int i = 0; i < adds.length; i++) {
StringBuilder stringBuilder = new StringBuilder("redis://");
adds[i] = stringBuilder.append(adds[i]).toString();
nodeAddress.add(adds[i]);
}
MasterSlaveServersConfig masterSlaveServersConfig = config.useMasterSlaveServers().setMasterAddress(adds[0]);
masterSlaveServersConfig.setSlaveAddresses(nodeAddress);
if (org.springframework.util.StringUtils.hasText(redissonProperties.getPassword())) {
masterSlaveServersConfig.setPassword(redissonProperties.getPassword());
}
if (StringUtils.hasText(redissonProperties.getDatabase())) {
masterSlaveServersConfig.setDatabase(Integer.parseInt(redissonProperties.getDatabase()));
}
masterSlaveClient = Redisson.create(config);
}
}
}
return masterSlaveClient;
}
}
哨兵
package com.microservice.stock.utils.redisson;
import com.microservice.stock.config.RedissonProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.springframework.util.StringUtils;
/**
* @author zzkk
* @desc 哨兵
* @date 2022/9/22 11:06
*/
public class Sentinel {
public static volatile RedissonClient sentinelClient = null;
public static synchronized RedissonClient getInstance(RedissonProperties redissonProperties) {
String type = "sentinel";
if (!type.equalsIgnoreCase(redissonProperties.getType())) {
return Cluster.getInstance(redissonProperties);
}
if (sentinelClient == null) {
synchronized (Sentinel.class) {
if (sentinelClient == null) {
Config config = new Config();
String[] adds = redissonProperties.getNodes().split(",");
for (int i = 0; i < adds.length; i++) {
StringBuilder stringBuilder = new StringBuilder("redis://");
adds[i] = stringBuilder.append(adds[i]).toString();
}
SentinelServersConfig serverConfig = config.useSentinelServers()
.addSentinelAddress(adds)
.setMasterName(redissonProperties.getMaster());
if (org.springframework.util.StringUtils.hasText(redissonProperties.getPassword())) {
serverConfig.setSentinelPassword(redissonProperties.getPassword());
}
if (StringUtils.hasText(redissonProperties.getDatabase())) {
serverConfig.setDatabase(Integer.parseInt(redissonProperties.getDatabase()));
}
sentinelClient = Redisson.create(config);
}
}
}
return sentinelClient;
}
}
集群
package com.microservice.stock.utils.redisson;
import com.microservice.stock.config.RedissonProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.springframework.util.StringUtils;
/**
* @author zzkk
* @desc 集群
* @date 2022/9/22 11:06
*/
public class Cluster {
public static volatile RedissonClient clusterClient = null;
public static synchronized RedissonClient getInstance(RedissonProperties redissonProperties) {
String type = "cluster";
if (!type.equalsIgnoreCase(redissonProperties.getType().trim())) {
return MasterSlave.getInstance(redissonProperties);
}
if (clusterClient == null) {
synchronized (Cluster.class) {
if (clusterClient == null) {
String[] nodesList = redissonProperties.getNodes().split(",");
for (int i = 0; i < nodesList.length; i++) {
StringBuilder stringBuilder = new StringBuilder("redis://");
nodesList[i] = stringBuilder.append(nodesList[i]).toString();
}
Config config = new Config();
ClusterServersConfig clusterServersConfig = config.useClusterServers().addNodeAddress(nodesList);
if (StringUtils.hasText(redissonProperties.getPassword())) {
clusterServersConfig.setPassword(redissonProperties.getPassword());
}
clusterClient = Redisson.create(config);
}
}
}
return clusterClient;
}
}
测试
@Resource
RedissonClient redissonClient;
@Test
public void redissionTest() throws InterruptedException {
StopWatch stopWatch = new StopWatch();
stopWatch.start("redission休眠看门口狗续约测试");
final String lockKey = "my_redission_key";
final RLock lock = redissonClient.getLock(lockKey);
if (lock.tryLock()) {
try {
System.out.println("加锁成功,等待观察redis过期时间是否自动续期");
TimeUnit.SECONDS.sleep(60);
System.out.println("休眠结束");
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
System.out.println("释放当前执行线程的锁");
lock.unlock();
}
System.out.println("解锁成功");
}
}
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
tryLock方法注意事项:
// 1、lock.tryLock(2, 40, TimeUnit.SECONDS) 自定义最大加锁时长,看门狗续约会失效;
// 可能发生加锁代码未执行完成,但锁已经过期,出现解锁异常,
// 或者解锁到其他线程加的锁(可以用lock.isHeldByCurrentThread()检查锁是否被当前线程持有);所以不建议使用自定义有效期
// 2、lock.tryLock(),使用看门狗自动续约机制(每隔10秒自动续约,默认30秒)
更多推荐
已为社区贡献1条内容
所有评论(0)