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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章