SpringBoot Redis 集群

配置参数

这里假设已经有了一个 Redis 的集群环境,项目中需要调整以下几个部分

  1. 修改配置参数,集群的节点和密码配置;
  2. 确保引入的 Jedis 版本支持设置密码,spring-data-redis 1.8 以上,SpringBoot 1.5 以上才支持设置密码;
  3. 注入 RedisTemplate
  4. 编写工具类;

    修改配置参数

    1. ############### Redis 集群配置 #########################
    2. spring.custome.redis.cluster.nodes=172.20.0.1:7001,172.20.0.2:7002,172.20.0.3:7003
    3. spring.custome.redis.cluster.max-redirects=3
    4. spring.custome.redis.cluster.max-active=500
    5. spring.custome.redis.cluster.max-wait=-1
    6. spring.custome.redis.cluster.max-idle=500
    7. spring.custome.redis.cluster.min-idle=20
    8. spring.custome.redis.cluster.timeout=3000
    9. spring.custome.redis.cluster.password=redis.cluster.password

    引入依赖(如果需要)

    确保 SpringBoot 的版本大于 1.4.x 如果不是的话,采用如下配置,先排除 SpringBoot 中旧版本 Jedisspring-data-redis,再依赖高版本的 Jedisspring-data-redis
    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-data-redis</artifactId>
    4. <!-- 1.4 版本 SpringBoot 中 Jedis 不支持密码登录 -->
    5. <exclusions>
    6. <exclusion>
    7. <groupId>redis.clients</groupId>
    8. <artifactId>jedis</artifactId>
    9. </exclusion>
    10. <exclusion>
    11. <groupId>org.springframework.data</groupId>
    12. <artifactId>spring-data-redis</artifactId>
    13. </exclusion>
    14. </exclusions>
    15. </dependency>
    16. <!-- 手动依赖 Jedis 和 spring-data-redis-->
    17. <dependency>
    18. <groupId>redis.clients</groupId>
    19. <artifactId>jedis</artifactId>
    20. <version>2.9.0</version>
    21. </dependency>
    22. <dependency>
    23. <groupId>org.springframework.data</groupId>
    24. <artifactId>spring-data-redis</artifactId>
    25. <version>1.8.0.RELEASE</version>
    26. </dependency>

    注入 RedisTemplate

    注入 RedisTemplate 需要三个组件,分别是JedisConnectionFactoryRedisClusterConfigurationJedisPoolConfig,下面是注入RedisTempalte 的代码。先根据配置创建 JedisConnectFactory 同时需要配置 RedisClusterConfigurationJedisPoolConfig,最后将JedisConnectionFactory 返回用于创建RedisTemplate
    1. import com.fasterxml.jackson.annotation.JsonAutoDetect;
    2. import com.fasterxml.jackson.annotation.PropertyAccessor;
    3. import com.fasterxml.jackson.databind.ObjectMapper;
    4. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    5. import org.springframework.beans.factory.annotation.Value;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Primary;
    8. import org.springframework.data.redis.connection.RedisClusterConfiguration;
    9. import org.springframework.data.redis.connection.RedisNode;
    10. import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
    11. import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    12. import org.springframework.data.redis.core.RedisTemplate;
    13. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    14. import org.springframework.data.redis.serializer.StringRedisSerializer;
    15. import java.time.Duration;
    16. import java.util.ArrayList;
    17. import java.util.List;
    18. public class RedisClusterConfig {
    19. @Bean(name = "redisTemplate")
    20. @Primary
    21. public RedisTemplate redisClusterTemplate(@Value("${spring.custome.redis.cluster.nodes}") String host,
    22. @Value("${spring.custome.redis.cluster.password}") String password,
    23. @Value("${spring.custome.redis.cluster.timeout}") long timeout,
    24. @Value("${spring.custome.redis.cluster.max-redirects}") int maxRedirect,
    25. @Value("${spring.custome.redis.cluster.max-active}") int maxActive,
    26. @Value("${spring.custome.redis.cluster.max-wait}") int maxWait,
    27. @Value("${spring.custome.redis.cluster.max-idle}") int maxIdle,
    28. @Value("${spring.custome.redis.cluster.min-idle}") int minIdle) {
    29. JedisConnectionFactory connectionFactory = jedisClusterConnectionFactory(host, password,
    30. timeout, maxRedirect, maxActive, maxWait, maxIdle, minIdle);
    31. return createRedisClusterTemplate(connectionFactory);
    32. }
    33. private JedisConnectionFactory jedisClusterConnectionFactory(String host, String password,
    34. long timeout, int maxRedirect, int maxActive, int maxWait, int maxIdle, int minIdle) {
    35. RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
    36. List<RedisNode> nodeList = new ArrayList<>();
    37. String[] cNodes = host.split(",");
    38. //分割出集群节点
    39. for (String node : cNodes) {
    40. String[] hp = node.split(":");
    41. nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1])));
    42. }
    43. redisClusterConfiguration.setClusterNodes(nodeList);
    44. redisClusterConfiguration.setPassword(password);
    45. redisClusterConfiguration.setMaxRedirects(maxRedirect);
    46. // 连接池通用配置
    47. GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
    48. genericObjectPoolConfig.setMaxIdle(maxIdle);
    49. genericObjectPoolConfig.setMaxTotal(maxActive);
    50. genericObjectPoolConfig.setMinIdle(minIdle);
    51. genericObjectPoolConfig.setMaxWaitMillis(maxWait);
    52. genericObjectPoolConfig.setTestWhileIdle(true);
    53. genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(300000);
    54. JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration
    55. .builder();
    56. builder.connectTimeout(Duration.ofSeconds(timeout));
    57. builder.usePooling();
    58. builder.poolConfig(genericObjectPoolConfig);
    59. JedisConnectionFactory connectionFactory = new JedisConnectionFactory(redisClusterConfiguration, builder.build());
    60. // 连接池初始化
    61. connectionFactory.afterPropertiesSet();
    62. return connectionFactory;
    63. }
    64. private RedisTemplate createRedisClusterTemplate(JedisConnectionFactory redisConnectionFactory) {
    65. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    66. redisTemplate.setConnectionFactory(redisConnectionFactory);
    67. Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
    68. ObjectMapper om = new ObjectMapper();
    69. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    70. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    71. jackson2JsonRedisSerializer.setObjectMapper(om);
    72. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    73. // key采用String的序列化方式
    74. redisTemplate.setKeySerializer(stringRedisSerializer);
    75. // hash的key也采用String的序列化方式
    76. redisTemplate.setHashKeySerializer(stringRedisSerializer);
    77. // value序列化方式采用jackson
    78. redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    79. // hash的value序列化方式采用jackson
    80. redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    81. redisTemplate.afterPropertiesSet();
    82. return redisTemplate;
    83. }
    84. }

    这里一定要注意 Jedis 的 Spring-data-redis 的版本支持设置密码,毕竟生产环境是一定要配置密码的。

编写工具类

其实到这里基本上已经完成了,可以看到 SpringBoot 项目接入 Redis 集群还是比较简单的,而且如果之前单机环境就是采用RedisTemplate 的话,现在也就不需要编写工具类,之前的操作依旧有效。工具类代码太长,只贴部分。

  1. /**
  2. * 删除KEY
  3. * @param key
  4. * @return
  5. */
  6. public boolean delete(String key) {
  7. try {
  8. return getTemplate().delete(key);
  9. } catch (Exception e) {
  10. log.error("redis hasKey() is error");
  11. return false;
  12. }
  13. }
  14. /**
  15. * 普通缓存获取
  16. *
  17. * @param key 键
  18. * @return 值
  19. */
  20. public Object get(String key) {
  21. return key == null ? null : getTemplate().opsForValue().get(key);
  22. }
  23. /**
  24. * 普通缓存放入
  25. *
  26. * @param key 键
  27. * @param value 值
  28. * @return true成功 false失败
  29. */
  30. public boolean set(String key, Object value) {
  31. try {
  32. getTemplate().opsForValue().set(key, value);
  33. return true;
  34. } catch (Exception e) {
  35. log.error("redis set() is error");
  36. return false;
  37. }
  38. }
  39. /**
  40. * 普通缓存放入并设置时间
  41. *
  42. * @param key 键
  43. * @param value 值
  44. * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  45. * @return true成功 false 失败
  46. */
  47. public boolean set(String key, Object value, long time) {
  48. try {
  49. if (time > 0) {
  50. getTemplate().opsForValue().set(key, value, time, TimeUnit.SECONDS);
  51. } else {
  52. set(key, value);
  53. }
  54. return true;
  55. } catch (Exception e) {
  56. log.error("redis set() is error");
  57. return false;
  58. }
  59. }
  60. /**
  61. * 计数器
  62. *
  63. * @param key 键
  64. * @return 值
  65. */
  66. public Long incr(String key) {
  67. return getTemplate().opsForValue().increment(key);
  68. }
  69. public Long incrBy(String key, long step) {
  70. return getTemplate().opsForValue().increment(key, step);
  71. }
  72. /**
  73. * HashGet
  74. *
  75. * @param key 键 不能为null
  76. * @param item 项 不能为null
  77. * @return 值
  78. */
  79. public Object hget(String key, String item) {
  80. return getTemplate().opsForHash().get(key, item);
  81. }
  82. /**
  83. * 获取hashKey对应的所有键值
  84. *
  85. * @param key 键
  86. * @return 对应的多个键值
  87. */
  88. public Map<Object, Object> hmget(String key) {
  89. return getTemplate().opsForHash().entries(key);
  90. }
  91. /**
  92. * 获取hashKey对应的批量键值
  93. * @param key
  94. * @param values
  95. * @return
  96. */
  97. public List<Object> hmget(String key, List<String> values) {
  98. return getTemplate().opsForHash().multiGet(key, values);
  99. }