线上碰到连接池无法获取问题 ,排查后,看到配置redis的地方有问题,
RedisConnectionFailureException: Could not get a resource from the pool; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
修改如下
先说下解释
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=admin
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
JAVA配置如下 主要是连接池空闲数和最大连接数,还有等待时间,默认连接数和等待数都是 8
之前可能就是因为空闲等待的连接数300 太大了,导致连接获取失败,每 个服务连接300 有8台服务器,就是2400连接数,巨坑
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.commandTimeout}")
private int commandTimeout;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.pool.max-active}")
private int maxActive;
@Bean
public JedisCluster getJedisCluster() {
String[] cNodes = clusterNodes.split(",");
Set nodes =new HashSet<>();
//分割出集群节点
for(String node : cNodes) {
String[] hp = node.split(":");
nodes.add(new HostAndPort(hp[0],Integer.parseInt(hp[1])));
}
JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
//创建集群对象
// JedisCluster jedisCluster = new JedisCluster(nodes,commandTimeout);
return new JedisCluster(nodes, 1000, timeout, 1000,
password, jedisPoolConfig);
/**
* 设置数据存入redis 的序列化方式
*redisTemplate序列化默认使用的jdkSerializeable,存储二进制字节码,导致key会出现乱码,所以自定义
*序列化类
*
* @paramredisConnectionFactory
*/
@Bean
public RedisTemplate
原理如下
SpringBootStarterRedis 源码分析
我们用 Spring Boot 都知道 starter 的原理(spring-boot-autoconfigure.jar 包里面的 spring.factories 定义了 Spring Boot 默认加载的 AutoConfiguration),因此,打开 spring.factories 文件可以找到 Spring 自动加载了。
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,
这两个 Configuration 类,我们先打开 RedisAutoConfiguration 的源码 ,来一起看一下里面的关键代码片段。
(1)代码片段一:自动加载 JedisConnectionFactory。
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
通过这一段代码可以看到,JedisConnectionFactory 可以自己配置也可以直接用 Spring Boot 给我们提供的默认配置。
(2)代码片段二:查看 createJedisConnectionFactory() 的具体方法。
private JedisConnectionFactory createJedisConnectionFactory() {
//这里会取我们配置文件里面的配置,如果没有配置,new 一个默认连接池
JedisPoolConfig poolConfig = this.properties.getPool() != null
? jedisPoolConfig() : new JedisPoolConfig();
//如果配置了Sentinel就取哨兵的配置直接返回
if (getSentinelConfig() != null) {
return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
}
//如果没有配置中Sentinel,而配置了Cluster切片的配置方法,它就取Cluster的配置方法
if (getClusterConfiguration() != null) {
return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
}
//默认取连接pool的配置方法
return new JedisConnectionFactory(poolConfig);
}
.......
//取配置文件里面的Pool的配置
private JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
RedisProperties.Pool props = this.properties.getPool();
config.setMaxTotal(props.getMaxActive());
config.setMaxIdle(props.getMaxIdle());
config.setMinIdle(props.getMinIdle());
config.setMaxWaitMillis(props.getMaxWait());
return config;
}
.......
//JedisPoolConfig的类默认构造函数
public class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
this.setTestWhileIdle(true);
this.setMinEvictableIdleTimeMillis(60000L);
this.setTimeBetweenEvictionRunsMillis(30000L);
this.setNumTestsPerEvictionRun(-1);
}
}
最终还是用到连接
RedisConnectionFactory 就是特于JedisConnectionFactory 创建之后的连接,
所以后面的 RedisTemplate
另外项目的配置
redis.pass=
#超时时间
redis.timeout=2000
#最大空闲连接数
redis.maxIdle=100
#最小空闲连接数
redis.minIdle=50
#最大连接数
redis.maxTotal=500
#最大等待时间
redis.maxWait=1500
#使用连接时,检测连接是否成功
redis.testOnBorrow=true
#返回连接时,检测连接是否成功
redis.testOnReturn=true
@Configuration
@PropertySource(value = "classpath:/redis.properties")
public class RedisConfiguration
{
@Bean(name = "jedis.cluster")
@Autowired
public JedisCluster jedisCluster(
@Qualifier("jedis.pool.config") GenericObjectPoolConfig config,
@Value("${redis.host.1}") String host_1,
@Value("${redis.port.1}") int port_1,
@Value("${redis.host.2}") String host_2,
@Value("${redis.port.2}") int port_2,
@Value("${redis.host.3}") String host_3,
@Value("${redis.port.3}") int port_3,
@Value("${redis.host.4}") String host_4,
@Value("${redis.port.4}") int port_4,
@Value("${redis.host.5}") String host_5,
@Value("${redis.port.5}") int port_5,
@Value("${redis.host.6}") String host_6,
@Value("${redis.port.6}") int port_6,
@Value("${redis.timeout}") int timeout)
{
Set nodes = new HashSet();
nodes.add(new HostAndPort(host_1, port_1));
nodes.add(new HostAndPort(host_2, port_2));
nodes.add(new HostAndPort(host_3, port_3));
nodes.add(new HostAndPort(host_4, port_4));
nodes.add(new HostAndPort(host_5, port_5));
nodes.add(new HostAndPort(host_6, port_6));
//zhangfeifei123!!==
return new JedisCluster(nodes, 1000, timeout, 1000,
"1qOaaqes", config);
}
@Bean(name = "jedis.pool.config")
public GenericObjectPoolConfig jedisPoolConfig(
@Value("${redis.maxTotal}") int maxTotal,
@Value("${redis.maxIdle}") int maxIdle,
@Value("${redis.minIdle}") int minIdle,
@Value("${redis.maxWait}") int maxWaitMillis,
@Value("${redis.testOnBorrow}") boolean testOnBorrow,
@Value("${redis.testOnReturn}") boolean testOnReturn)
{
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
config.setMaxWaitMillis(maxWaitMillis);
config.setTestOnBorrow(testOnBorrow);
config.setTestOnReturn(testOnReturn);
return config;
}
}