SpringBoot Redis自適應配置的實現(Cluster Standalone Sentinel)

核心代碼段

提供一個JedisConnectionFactory  根據配置來判斷 單點 集群 還是哨兵

 @Bean
    @ConditionalOnMissingBean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory factory = null;
 
        String[] split = node.split(",");
        Set<HostAndPort> nodes = new LinkedHashSet<>();
        for (int i = 0; i < split.length; i++) {
            try {
                String[] split1 = split[i].split(":");
                nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1])));
            } catch (Exception e) {
                throw new RuntimeException(String.format("出現配置錯誤!請確認node=[%s]是否正確", node));
            }
        }
        //獲得默認的連接池構造器(怎麼設計的,為什麼不抽象出單獨類,供用戶使用呢) 有毒
        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
                (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
        //指定jedisPoolConifig來修改默認的連接池構造器(真麻煩,濫用設計模式!) !!!!
        jpcb.poolConfig(jedisPoolConfig);
        //通過構造器來構造jedis客戶端配置
        JedisClientConfiguration jedisClientConfiguration = jpcb.build();
 
        //如果是哨兵的模式
        if (!StringUtils.isEmpty(sentinel)) {
            logger.info("Redis use SentinelConfiguration");
            RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
            String[] sentinelArray = sentinel.split(",");
            for (String s : sentinelArray) {
                try {
                    String[] split1 = s.split(":");
                    redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
                } catch (Exception e) {
                    throw new RuntimeException(String.format("出現配置錯誤!請確認node=[%s]是否正確", node));
                }
            }
            factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
 
        }
        //如果是單個節點 用Standalone模式
        else if (nodes.size() == 1) {
            logger.info("Redis use RedisStandaloneConfiguration");
            for (HostAndPort n : nodes) {
                RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
                if (!StringUtils.isEmpty(password)) {
                    redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
                }
                redisStandaloneConfiguration.setPort(n.getPort());
                redisStandaloneConfiguration.setHostName(n.getHost());
                factory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
            }
        }
        //集群配置信息實現
        else {
            logger.info("Redis use RedisStandaloneConfiguration");
            RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
            nodes.forEach(n -> {
                redisClusterConfiguration.addClusterNode(new RedisNode(n.getHost(), n.getPort()));
            });
            if (!StringUtils.isEmpty(password)) {
                redisClusterConfiguration.setPassword(RedisPassword.of(password));
            }
            redisClusterConfiguration.setMaxRedirects(maxRedirect);
            factory = new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
        }
        return factory;
    }

Configration

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
 
import java.util.LinkedHashSet;
import java.util.Set;
 
/**
 * @Author foxzzz
 * @Class SelfAdaptionRedisConfig
 * @Description 自適應redis配置
 * 適用於 單點[主從]  哨兵模式  集群模式
 * @Date 2020/7/6 14:34
 */
 
@Configuration
@EnableCaching
public class SelfAdaptionRedisConfig<K, V> extends CachingConfigurerSupport {
    private final static Logger logger = LoggerFactory.getLogger(SelfAdaptionRedisConfig.class);
 
    @Value("${spring.redis.node}")
    private String node;
    @Value("${spring.redis.timeout:0}")
    private int timeout;
    @Value("${spring.redis.password:}")
    private String password;
    @Value("${spring.redis.sentinel:}")
    private String sentinel;
 
    @Value("${spring.redis.jedis.pool.max-total:8}")
    private int maxTotal;
    @Value("${spring.redis.jedis.pool.max-idle:8}")
    private int maxIdle;
    @Value("${spring.redis.jedis.pool.min-idle:0}")
    private int minIdle;
    @Value("${spring.redis.jedis.pool.max-wait:-1}")
    private long maxWaitMillis;
    @Value("${spring.redis.jedis.pool.test-on-borrow:true}")
    private boolean testOnBorrow;
    @Value("${spring.redis.jedis.factory.max-redirects:5}")
    private int maxRedirect;
 
 
    @Autowired
    private JedisPoolConfig jedisPoolConfig;
    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;
 
 
    @Bean
    @ConditionalOnMissingBean
    @Override
    public CacheManager cacheManager() {
        // 初始化緩存管理器,在這裡我們可以緩存的整體過期時間什麼的,我這裡默認沒有配置
        logger.info("初始化 -> [{}]", "CacheManager RedisCacheManager Start");
        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
                .RedisCacheManagerBuilder
                .fromConnectionFactory(jedisConnectionFactory);
        return builder.build();
    }
 
 
    @Bean
    @ConditionalOnMissingBean
    @Override
    public CacheErrorHandler errorHandler() {
        // 異常處理,當Redis發生異常時,打印日志,但是程序正常走
        logger.info("初始化 -> [{}]", "Redis CacheErrorHandler");
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                logger.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
            }
 
            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                logger.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
            }
 
            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                logger.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
            }
 
            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                logger.error("Redis occur handleCacheClearError:", e);
            }
        };
        return cacheErrorHandler;
    }
 
 
    @Bean
    @ConditionalOnMissingBean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        // 獲取連接時的最大等待毫秒數(如果設置為阻塞時BlockWhenExhausted),如果超時就拋異常, 小於零:阻塞不確定的時間,  默認-1
        config.setMaxWaitMillis(maxWaitMillis);
        //最小空閑連接數, 默認0
        config.setMinIdle(minIdle);
        // 最大空閑連接數, 默認8個
        config.setMaxIdle(maxIdle);
        // 最大連接數, 默認值8個
        config.setMaxTotal(maxTotal);
        //對拿到的connection進行validateObject校驗
        config.setTestOnBorrow(testOnBorrow);
        return config;
    }
 
//    private JedisCluster getJedisCluster() {
//        String[] split = node.split(",");
//        Set<HostAndPort> nodes = new LinkedHashSet<>();
//        for (int i = 0; i < split.length; i++) {
//            try {
//                String[] split1 = split[i].split(":");
//                nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1]))));
//            } catch (Exception e) {
//            }
//        }
//        JedisCluster jedisCluster = null;
//        if (StringUtils.isEmpty(password)) {
//            jedisCluster = new JedisCluster(nodes, 5000, 3000, 10, jedisPoolConfig);
//        } else {
//            jedisCluster = new JedisCluster(nodes, 5000, 3000, 10, password, jedisPoolConfig);
//        }
//    }
 
 
    @Bean
    @ConditionalOnMissingBean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory factory = null;
 
        String[] split = node.split(",");
        Set<HostAndPort> nodes = new LinkedHashSet<>();
        for (int i = 0; i < split.length; i++) {
            try {
                String[] split1 = split[i].split(":");
                nodes.add(new HostAndPort(split1[0], Integer.parseInt(split1[1])));
            } catch (Exception e) {
                throw new RuntimeException(String.format("出現配置錯誤!請確認node=[%s]是否正確", node));
            }
        }
        //獲得默認的連接池構造器(怎麼設計的,為什麼不抽象出單獨類,供用戶使用呢) 有毒
        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
                (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
        //指定jedisPoolConifig來修改默認的連接池構造器(真麻煩,濫用設計模式!) !!!!
        jpcb.poolConfig(jedisPoolConfig);
        //通過構造器來構造jedis客戶端配置
        JedisClientConfiguration jedisClientConfiguration = jpcb.build();
 
        //如果是哨兵的模式
        if (!StringUtils.isEmpty(sentinel)) {
            logger.info("Redis use SentinelConfiguration");
            RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
            String[] sentinelArray = sentinel.split(",");
            for (String s : sentinelArray) {
                try {
                    String[] split1 = s.split(":");
                    redisSentinelConfiguration.addSentinel(new RedisNode(split1[0], Integer.parseInt(split1[1])));
                } catch (Exception e) {
                    throw new RuntimeException(String.format("出現配置錯誤!請確認node=[%s]是否正確", node));
                }
            }
            factory = new JedisConnectionFactory(redisSentinelConfiguration, jedisClientConfiguration);
 
        }
        //如果是單個節點 用Standalone模式
        else if (nodes.size() == 1) {
            logger.info("Redis use RedisStandaloneConfiguration");
            for (HostAndPort n : nodes) {
                RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
                if (!StringUtils.isEmpty(password)) {
                    redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
                }
                redisStandaloneConfiguration.setPort(n.getPort());
                redisStandaloneConfiguration.setHostName(n.getHost());
                factory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
            }
        }
        //集群配置信息實現
        else {
            logger.info("Redis use RedisStandaloneConfiguration");
            RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
            nodes.forEach(n -> {
                redisClusterConfiguration.addClusterNode(new RedisNode(n.getHost(), n.getPort()));
            });
            if (!StringUtils.isEmpty(password)) {
                redisClusterConfiguration.setPassword(RedisPassword.of(password));
            }
            redisClusterConfiguration.setMaxRedirects(maxRedirect);
            factory = new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
        }
        return factory;
    }
 
 
    @Bean
    @ConditionalOnMissingBean
    public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        //設置序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

pom依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>${springboot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${redis.version}</version>
        </dependency>
 
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

配置文件

1.yml配置

1.1支持 Cluster模式 集群

  • node有多個redis的地址,以逗號分隔
  • 如果redis沒有密碼直接去掉配置就可以瞭 yml配置
spring:
  redis:
    node: 127.0.0.1:1001,127.0.0.1:1002,127.0.0.1:1003
    password: 123456

properties配置

  • spring.redis.node=127.0.0.1:1001,127.0.0.1:1002,127.0.0.1:1003
  • spring.redis.password=123456

1.2支持 Standalone模式 單點

node裡隻有一個redis地址的時候,會自動變成單點模式 yml配置

spring:
  redis:
    node: 127.0.0.1:1001
    password: 123456    

properties配置

spring.redis.node=127.0.0.1:1001
spring.redis.password=123456

1.3支持 Sentinel模式 哨兵

當配置上sentinel以後就變成瞭哨兵模式 多個哨兵可以以逗號分割 yml配置

spring:
  redis:
    node: 127.0.0.1:1001
    sentinel:127.0.0.1:1002,127.0.0.1:1003
    password: 123456    

properties配置

spring.redis.node=127.0.0.1:1001
spring.redis.sentinel:127.0.0.1:1002,127.0.0.1:1003
spring.redis.password=123456

1.4覆蓋默認配置

如果沒有配置這些信息,就會走默認配置 也可以在properties或者yml覆蓋默認配置

#最大連接數, 默認值8個
spring.redis.jedis.pool.max-total=8
#最大空閑連接數, 默認8個
spring.redis.jedis.pool.max-idle=8
#最小空閑連接數, 默認0
spring.redis.jedis.pool.min-idle=0
#獲取連接時的最大等待毫秒數,如果超時就拋異常, 小於零:阻塞不確定的時間,  默認-1
spring.redis.jedis.pool.max-wait=-1
#對拿到的connection進行validateObject校驗
spring.redis.jedis.pool.test-on-borrow=true
#集群時最大重定向個數默認5
spring.redis.jedis.factory.max-redirects=5

使用 

代碼使用

 @Autowired
 private RedisTemplate<String, Object> redisTemplate;

到此這篇關於SpringBoot Redis自適應配置的實現(Cluster Standalone Sentinel)的文章就介紹到這瞭,更多相關SpringBoot Redis自適應配置內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: