redis+springboot1.5.6集群配置

线上碰到连接池无法获取问题 ,排查后,看到配置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<HostAndPort> 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 的序列化方式
         *</br>redisTemplate序列化默认使用的jdkSerializeable,存储二进制字节码,导致key会出现乱码,所以自定义
         *序列化类
         *
         * @paramredisConnectionFactory
         */
        @Bean
        public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {
            RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper objectMapper =new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setKeySerializer(new StringRedisSerializer());

            redisTemplate.afterPropertiesSet();

            return 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<Object,Object> redisTemplate = new RedisTemplate<>(); 模板方法使用方式是一样的,这里JedisConnection可以自己配置,如果不配置就是默认配置

另外项目的配置

 

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<HostAndPort> nodes = new HashSet<HostAndPort>();
        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;
    }

}

 

发布了144 篇原创文章 · 获赞 28 · 访问量 16万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章