修复:1、redis报错、统一了所有模式(单机、集群、主从、哨兵)的配置参数:都设置了 retryAttempts, retryInterval, timeout, connectTimeout, idleConnectionTimeout, 连接池大小等。3、地址自动补齐协议前缀:normalizeAddress() 方法自动添加 redis://,用户配置可以省略, 其他等等。

This commit is contained in:
高雄
2026-04-21 14:08:16 +08:00
parent 171762d676
commit e7fe1afe19
2 changed files with 189 additions and 233 deletions

View File

@@ -1,6 +1,5 @@
package cn.keking.config; package cn.keking.config;
import io.netty.channel.nio.NioEventLoopGroup;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson; import org.redisson.Redisson;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
@@ -13,8 +12,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
* Redisson 客户端配置 * Redisson 客户端配置(完善版)
* Created by kl on 2017/09/26. * 支持 single / cluster / master-slave / sentinel 四种模式,配置完整,统一参数。
*/ */
@ConditionalOnExpression("'${cache.type:default}'.equals('redis')") @ConditionalOnExpression("'${cache.type:default}'.equals('redis')")
@ConfigurationProperties(prefix = "spring.redisson") @ConfigurationProperties(prefix = "spring.redisson")
@@ -22,114 +21,71 @@ import org.springframework.util.ClassUtils;
public class RedissonConfig { public class RedissonConfig {
// ========================== 连接配置 ========================== // ========================== 连接配置 ==========================
private static String address; private String address;
private static String password; private String password;
private static String clientName; private String clientName;
private static int database = 0; private int database = 0;
private static String mode = "single"; private String mode = "single";
private static String masterName = "kkfile"; private String masterName = "kkfile";
// ========================== 超时配置 ========================== // ========================== 超时配置 ==========================
private static int idleConnectionTimeout = 10000; private int idleConnectionTimeout = 10000;
private static int connectTimeout = 10000; private int connectTimeout = 10000;
private static int timeout = 3000; private int timeout = 3000;
// ========================== 重试配置 ========================== // ========================== 重试配置 ==========================
private static int retryAttempts = 3; private int retryAttempts = 3;
private static int retryInterval = 1500; private int retryInterval = 1500;
// ========================== 连接池配置 ========================== // ========================== 连接池配置 ==========================
private static int connectionMinimumIdleSize = 10; private int connectionMinimumIdleSize = 10;
private static int connectionPoolSize = 64; private int connectionPoolSize = 64;
private static int subscriptionsPerConnection = 5; private int subscriptionsPerConnection = 5;
private static int subscriptionConnectionMinimumIdleSize = 1; private int subscriptionConnectionMinimumIdleSize = 1;
private static int subscriptionConnectionPoolSize = 50; private int subscriptionConnectionPoolSize = 50;
// ========================== 集群专用配置 ==========================
private int scanInterval = 2000;
// ========================== 其他配置 ========================== // ========================== 其他配置 ==========================
private static int dnsMonitoringInterval = 5000; private int dnsMonitoringInterval = 5000;
private static int thread; // 当前处理核数 * 2 private int threads; // 默认为0表示使用 CPU 核数 * 2
private static String codec = "org.redisson.codec.JsonJacksonCodec"; private String codec = "org.redisson.codec.JsonJacksonCodec";
@Bean @Bean
public static RedissonClient config() throws Exception { public RedissonClient redissonClient() {
Config config = new Config(); Config config = new Config();
// 密码处理 // 密码处理:空字符串转为 null
if (StringUtils.isBlank(password)) { String pwd = StringUtils.isBlank(password) ? null : password;
password = null;
}
// 根据模式创建对应的 Redisson 配置 // 根据模式构建配置
switch (mode) { switch (mode.toLowerCase()) {
case "cluster": case "cluster":
configureClusterMode(config); configureClusterMode(config, pwd);
break; break;
case "master-slave": case "master-slave":
configureMasterSlaveMode(config); configureMasterSlaveMode(config, pwd);
break; break;
case "sentinel": case "sentinel":
configureSentinelMode(config); configureSentinelMode(config, pwd);
break; break;
default: default:
configureSingleMode(config); configureSingleMode(config, pwd);
break; break;
} }
// 公共配置:编码器、线程数
applyCommonConfig(config);
return Redisson.create(config); return Redisson.create(config);
} }
// ========================== 配置方法 ========================== // ========================== 配置方法 ==========================
/** private void configureSingleMode(Config config, String pwd) {
* 配置集群模式 String normalizedAddress = normalizeAddress(address);
*/
private static void configureClusterMode(Config config) {
String[] clusterAddresses = address.split(",");
config.useClusterServers()
.setScanInterval(2000)
.addNodeAddress(clusterAddresses)
.setPassword(password)
.setRetryAttempts(retryAttempts)
.setTimeout(timeout)
.setMasterConnectionPoolSize(100)
.setSlaveConnectionPoolSize(100);
}
/**
* 配置主从模式
*/
private static void configureMasterSlaveMode(Config config) {
String[] masterSlaveAddresses = address.split(",");
validateMasterSlaveAddresses(masterSlaveAddresses);
String[] slaveAddresses = new String[masterSlaveAddresses.length - 1];
System.arraycopy(masterSlaveAddresses, 1, slaveAddresses, 0, slaveAddresses.length);
config.useMasterSlaveServers()
.setDatabase(database)
.setPassword(password)
.setMasterAddress(masterSlaveAddresses[0])
.addSlaveAddress(slaveAddresses);
}
/**
* 配置哨兵模式
*/
private static void configureSentinelMode(Config config) {
String[] sentinelAddresses = address.split(",");
config.useSentinelServers()
.setDatabase(database)
.setPassword(password)
.setMasterName(masterName)
.addSentinelAddress(sentinelAddresses);
}
/**
* 配置单机模式
*/
private static void configureSingleMode(Config config) throws Exception {
config.useSingleServer() config.useSingleServer()
.setAddress(address) .setAddress(normalizedAddress)
.setConnectionMinimumIdleSize(connectionMinimumIdleSize) .setConnectionMinimumIdleSize(connectionMinimumIdleSize)
.setConnectionPoolSize(connectionPoolSize) .setConnectionPoolSize(connectionPoolSize)
.setDatabase(database) .setDatabase(database)
@@ -143,183 +99,184 @@ public class RedissonConfig {
.setTimeout(timeout) .setTimeout(timeout)
.setConnectTimeout(connectTimeout) .setConnectTimeout(connectTimeout)
.setIdleConnectionTimeout(idleConnectionTimeout) .setIdleConnectionTimeout(idleConnectionTimeout)
.setPassword(StringUtils.trimToNull(password)); .setPassword(pwd);
// 设置编码器
Class<?> codecClass = ClassUtils.forName(getCodec(), ClassUtils.getDefaultClassLoader());
Codec codecInstance = (Codec) codecClass.getDeclaredConstructor().newInstance();
config.setCodec(codecInstance);
// 设置线程和事件循环组
config.setThreads(thread);
config.setEventLoopGroup(new NioEventLoopGroup());
} }
/** private void configureClusterMode(Config config, String pwd) {
* 验证主从模式地址 String[] nodeAddresses = normalizeAddresses(address.split(","));
*/ config.useClusterServers()
private static void validateMasterSlaveAddresses(String[] addresses) { .setScanInterval(scanInterval)
if (addresses.length == 1) { .addNodeAddress(nodeAddresses)
throw new IllegalArgumentException( .setPassword(pwd)
"redis.redisson.address MUST have multiple redis addresses for master-slave mode."); .setRetryAttempts(retryAttempts)
.setRetryInterval(retryInterval)
.setTimeout(timeout)
.setConnectTimeout(connectTimeout)
.setIdleConnectionTimeout(idleConnectionTimeout)
.setMasterConnectionPoolSize(connectionPoolSize)
.setSlaveConnectionPoolSize(connectionPoolSize)
.setSubscriptionConnectionPoolSize(subscriptionConnectionPoolSize)
.setSubscriptionConnectionMinimumIdleSize(subscriptionConnectionMinimumIdleSize)
.setSubscriptionsPerConnection(subscriptionsPerConnection)
.setClientName(clientName);
}
private void configureMasterSlaveMode(Config config, String pwd) {
String[] addresses = address.split(",");
validateMasterSlaveAddresses(addresses);
String[] normalizedAddresses = normalizeAddresses(addresses);
String masterAddress = normalizedAddresses[0];
String[] slaveAddresses = new String[normalizedAddresses.length - 1];
System.arraycopy(normalizedAddresses, 1, slaveAddresses, 0, slaveAddresses.length);
config.useMasterSlaveServers()
.setDatabase(database)
.setPassword(pwd)
.setMasterAddress(masterAddress)
.addSlaveAddress(slaveAddresses)
.setRetryAttempts(retryAttempts)
.setRetryInterval(retryInterval)
.setTimeout(timeout)
.setConnectTimeout(connectTimeout)
.setIdleConnectionTimeout(idleConnectionTimeout)
.setMasterConnectionPoolSize(connectionPoolSize)
.setSlaveConnectionPoolSize(connectionPoolSize)
.setSubscriptionConnectionPoolSize(subscriptionConnectionPoolSize)
.setSubscriptionConnectionMinimumIdleSize(subscriptionConnectionMinimumIdleSize)
.setSubscriptionsPerConnection(subscriptionsPerConnection)
.setClientName(clientName);
}
private void configureSentinelMode(Config config, String pwd) {
String[] sentinelAddresses = normalizeAddresses(address.split(","));
config.useSentinelServers()
.setDatabase(database)
.setPassword(pwd)
.setMasterName(masterName)
.addSentinelAddress(sentinelAddresses)
.setRetryAttempts(retryAttempts)
.setRetryInterval(retryInterval)
.setTimeout(timeout)
.setConnectTimeout(connectTimeout)
.setIdleConnectionTimeout(idleConnectionTimeout)
.setMasterConnectionPoolSize(connectionPoolSize)
.setSlaveConnectionPoolSize(connectionPoolSize)
.setSubscriptionConnectionPoolSize(subscriptionConnectionPoolSize)
.setSubscriptionConnectionMinimumIdleSize(subscriptionConnectionMinimumIdleSize)
.setSubscriptionsPerConnection(subscriptionsPerConnection)
.setClientName(clientName);
}
private void applyCommonConfig(Config config) {
// 设置编码器
if (StringUtils.isNotBlank(codec)) {
try {
Class<?> codecClass = ClassUtils.forName(codec, ClassUtils.getDefaultClassLoader());
Codec codecInstance = (Codec) codecClass.getDeclaredConstructor().newInstance();
config.setCodec(codecInstance);
} catch (Exception e) {
throw new IllegalStateException("Failed to create Redisson codec: " + codec, e);
}
}
// 设置线程数大于0时生效否则Redisson使用默认值CPU核数*2
if (threads > 0) {
config.setThreads(threads);
} }
} }
// ========================== Getter和Setter方法 ========================== // ========================== 辅助方法 ==========================
// 连接配置 /**
public String getAddress() { * 自动补齐 Redis 地址协议前缀redis:// 或 rediss://
return address; */
private String normalizeAddress(String addr) {
if (addr == null) {
return null;
}
addr = addr.trim();
if (!addr.startsWith("redis://") && !addr.startsWith("rediss://")) {
addr = "redis://" + addr;
}
return addr;
} }
public void setAddress(String address) { private String[] normalizeAddresses(String[] addresses) {
RedissonConfig.address = address; String[] normalized = new String[addresses.length];
for (int i = 0; i < addresses.length; i++) {
normalized[i] = normalizeAddress(addresses[i]);
}
return normalized;
} }
public String getPassword() { private void validateMasterSlaveAddresses(String[] addresses) {
return password; if (addresses.length < 2) {
throw new IllegalArgumentException(
"Master-slave mode requires at least 2 addresses: master and at least one slave. " +
"Current addresses: " + String.join(",", addresses));
}
} }
public void setPassword(String password) { // ========================== Getter / Setter供 Spring 绑定配置) ==========================
RedissonConfig.password = password; // 以下所有字段都需要提供 getter/setter示例中只列出关键字段实际使用时请补全所有字段。
} // 建议使用 Lombok @Data 或 IDE 自动生成。这里只展示部分,避免篇幅过长。
public String getClientName() { public String getAddress() { return address; }
return clientName; public void setAddress(String address) { this.address = address; }
}
public void setClientName(String clientName) { public String getPassword() { return password; }
RedissonConfig.clientName = clientName; public void setPassword(String password) { this.password = password; }
}
public int getDatabase() { public String getClientName() { return clientName; }
return database; public void setClientName(String clientName) { this.clientName = clientName; }
}
public void setDatabase(int database) { public int getDatabase() { return database; }
RedissonConfig.database = database; public void setDatabase(int database) { this.database = database; }
}
public static String getMode() { public String getMode() { return mode; }
return mode; public void setMode(String mode) { this.mode = mode; }
}
public void setMode(String mode) { public String getMasterName() { return masterName; }
RedissonConfig.mode = mode; public void setMasterName(String masterName) { this.masterName = masterName; }
}
public static String getMasterNamee() { public int getIdleConnectionTimeout() { return idleConnectionTimeout; }
return masterName; public void setIdleConnectionTimeout(int idleConnectionTimeout) { this.idleConnectionTimeout = idleConnectionTimeout; }
}
public void setMasterNamee(String masterName) { public int getConnectTimeout() { return connectTimeout; }
RedissonConfig.masterName = masterName; public void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; }
}
// 超时配置 public int getTimeout() { return timeout; }
public int getIdleConnectionTimeout() { public void setTimeout(int timeout) { this.timeout = timeout; }
return idleConnectionTimeout;
}
public void setIdleConnectionTimeout(int idleConnectionTimeout) { public int getRetryAttempts() { return retryAttempts; }
RedissonConfig.idleConnectionTimeout = idleConnectionTimeout; public void setRetryAttempts(int retryAttempts) { this.retryAttempts = retryAttempts; }
}
public int getConnectTimeout() { public int getRetryInterval() { return retryInterval; }
return connectTimeout; public void setRetryInterval(int retryInterval) { this.retryInterval = retryInterval; }
}
public void setConnectTimeout(int connectTimeout) { public int getConnectionMinimumIdleSize() { return connectionMinimumIdleSize; }
RedissonConfig.connectTimeout = connectTimeout; public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) { this.connectionMinimumIdleSize = connectionMinimumIdleSize; }
}
public int getTimeout() { public int getConnectionPoolSize() { return connectionPoolSize; }
return timeout; public void setConnectionPoolSize(int connectionPoolSize) { this.connectionPoolSize = connectionPoolSize; }
}
public void setTimeout(int timeout) { public int getSubscriptionsPerConnection() { return subscriptionsPerConnection; }
RedissonConfig.timeout = timeout; public void setSubscriptionsPerConnection(int subscriptionsPerConnection) { this.subscriptionsPerConnection = subscriptionsPerConnection; }
}
// 重试配置 public int getSubscriptionConnectionMinimumIdleSize() { return subscriptionConnectionMinimumIdleSize; }
public int getRetryAttempts() { public void setSubscriptionConnectionMinimumIdleSize(int subscriptionConnectionMinimumIdleSize) { this.subscriptionConnectionMinimumIdleSize = subscriptionConnectionMinimumIdleSize; }
return retryAttempts;
}
public void setRetryAttempts(int retryAttempts) { public int getSubscriptionConnectionPoolSize() { return subscriptionConnectionPoolSize; }
RedissonConfig.retryAttempts = retryAttempts; public void setSubscriptionConnectionPoolSize(int subscriptionConnectionPoolSize) { this.subscriptionConnectionPoolSize = subscriptionConnectionPoolSize; }
}
public int getRetryInterval() { public int getScanInterval() { return scanInterval; }
return retryInterval; public void setScanInterval(int scanInterval) { this.scanInterval = scanInterval; }
}
public void setRetryInterval(int retryInterval) { public int getDnsMonitoringInterval() { return dnsMonitoringInterval; }
RedissonConfig.retryInterval = retryInterval; public void setDnsMonitoringInterval(int dnsMonitoringInterval) { this.dnsMonitoringInterval = dnsMonitoringInterval; }
}
// 连接池配置 public int getThreads() { return threads; }
public int getConnectionMinimumIdleSize() { public void setThreads(int threads) { this.threads = threads; }
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) { public String getCodec() { return codec; }
RedissonConfig.connectionMinimumIdleSize = connectionMinimumIdleSize; public void setCodec(String codec) { this.codec = codec; }
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
RedissonConfig.connectionPoolSize = connectionPoolSize;
}
public int getSubscriptionsPerConnection() {
return subscriptionsPerConnection;
}
public void setSubscriptionsPerConnection(int subscriptionsPerConnection) {
RedissonConfig.subscriptionsPerConnection = subscriptionsPerConnection;
}
public int getSubscriptionConnectionMinimumIdleSize() {
return subscriptionConnectionMinimumIdleSize;
}
public void setSubscriptionConnectionMinimumIdleSize(int subscriptionConnectionMinimumIdleSize) {
RedissonConfig.subscriptionConnectionMinimumIdleSize = subscriptionConnectionMinimumIdleSize;
}
public int getSubscriptionConnectionPoolSize() {
return subscriptionConnectionPoolSize;
}
public void setSubscriptionConnectionPoolSize(int subscriptionConnectionPoolSize) {
RedissonConfig.subscriptionConnectionPoolSize = subscriptionConnectionPoolSize;
}
// 其他配置
public int getDnsMonitoringInterval() {
return dnsMonitoringInterval;
}
public void setDnsMonitoringInterval(int dnsMonitoringInterval) {
RedissonConfig.dnsMonitoringInterval = dnsMonitoringInterval;
}
public int getThread() {
return thread;
}
public void setThread(int thread) {
RedissonConfig.thread = thread;
}
public static String getCodec() {
return codec;
}
public void setCodec(String codec) {
RedissonConfig.codec = codec;
}
} }

View File

@@ -1,11 +1,9 @@
package cn.keking.service.cache.impl; package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService; import cn.keking.service.cache.CacheService;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue; import org.redisson.api.RBlockingQueue;
import org.redisson.api.RMapCache; import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -23,8 +21,9 @@ public class CacheServiceRedisImpl implements CacheService {
private final RedissonClient redissonClient; private final RedissonClient redissonClient;
public CacheServiceRedisImpl(Config config) { // 直接注入 Spring 容器中的 RedissonClient Bean
this.redissonClient = Redisson.create(config); public CacheServiceRedisImpl(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
} }
@Override @Override