瑞星 发表于 2024-10-9 20:28:24

Spring Boot 3 配置 Redis 兼容单例和集群

配置项

Spring Boot 3.x 的 redis 配置和 Spring Boot 2.x 是不一样的, 路径多了一个data
spring:
...
data:
    redis:
      host: @redis.host@
      port: @redis.port@
      password: @redis.password@
      database: @redis.database@兼容单例和集群的配置

开发时一般用一个Redis单例就足够, 测试和生产环境再换成集群, 但是在application.yml中默认的 Redis 单例和集群配置格式是不同的, 如果要用同一套格式兼容两种配置, 需要自定义 RedisConnectionFactory 这个bean的初始化.
@Configuration
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    public String host;
    @Value("${spring.data.redis.port}")
    public int port;
    @Value("${spring.data.redis.password}")
    public String password;
    @Value("${spring.data.redis.database}")
    public int database;

    @Bean
    public RedisTemplate<String, String> redisStringTemplate(RedisConnectionFactory redisConnectionFactory) {
      RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
      redisTemplate.setConnectionFactory(redisConnectionFactory);
      redisTemplate.setDefaultSerializer(new StringRedisSerializer());
      return redisTemplate;
    }

    @Bean
    public RedisTemplate<String, byte[]> redisBytesTemplate(RedisConnectionFactory redisConnectionFactory) {
      RedisTemplate<String, byte[]> redisTemplate = new RedisTemplate<>();
      redisTemplate.setConnectionFactory(redisConnectionFactory);

      RedisSerializer<String> redisKeySerializer = new StringRedisSerializer();
      redisTemplate.setKeySerializer(redisKeySerializer);
      redisTemplate.setHashKeySerializer(redisKeySerializer);
      redisTemplate.setValueSerializer(RedisSerializer.byteArray());
      redisTemplate.setHashValueSerializer(RedisSerializer.byteArray());

      return redisTemplate;
    }

    @Bean
    public RedisConnectionFactory lettuceConnectionFactory() {
      if (host.contains(",")) {
            RedisClusterConfiguration config = new RedisClusterConfiguration(Arrays.asList(host.split(",")));
            config.setMaxRedirects(3);
            if (password != null && !password.isEmpty()) {
                config.setPassword(RedisPassword.of(password));
            }
            LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
            factory.afterPropertiesSet();
            return factory;

      } else {
            RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
            config.setHostName(host);
            config.setPort(port);
            config.setDatabase(database);
            if (password != null && !password.isEmpty()) {
                config.setPassword(RedisPassword.of(password));
            }
            LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
            factory.afterPropertiesSet();
            return factory;
      }
    }

}如许, 当配置改为集群时, 只需要修改 spring.data.redis.host 的内容为 1.1.1.1:6379,1.1.1.2:6379,1.1.1.3:6379如许的格式就可以了.
使用 Byte 作为值存储

ByteUtil.java
public class ByteUtil {

    public static byte[] toByte(String str) {
      if (str == null) return null;
      return str.getBytes();
    }

    public static byte[][] toByte(String[] strs) {
      if (strs == null) return null;
      byte[][] arr = new byte[];
      for (int i = 0; i < strs.length; i++) {
            arr = strs.getBytes();
      }
      return arr;
    }

    public static String toString(byte[] bytes) {
      return bytes == null ? null : new String(bytes);
    }

    public static Set<String> toString(Set<byte[]> byteset) {
      if (byteset == null) return null;
      return byteset.stream()
                .map(String::new)
                .collect(Collectors.toSet());
    }

    public static List<String> toStrings(List<byte[]> byteslist) {
      if (byteslist == null) return null;
      return byteslist.stream()
                .map(String::new)
                .collect(Collectors.toList());
    }

    public static byte[] toByte(int x) {
      ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
      buffer.putInt(x);
      return buffer.array();
    }
    public static int toInteger(byte[] bytes) {
      ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
      buffer.put(bytes);
      buffer.flip();//need flip
      return buffer.getInt();
    }

    public static byte[] toByte(long x) {
      ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
      buffer.putLong(x);
      return buffer.array();
    }
    public static long toLong(byte[] bytes) {
      ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
      buffer.put(bytes);
      buffer.flip();//need flip
      return buffer.getLong();
    }

    public static byte[] toByte(Object object) {
      if (object == null) return null;
      try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(object);
            return baos.toByteArray();
      } catch (IOException e) {
            throw new RuntimeException(e);
      }
    }

    public static <T> List<T> toObjs(List<byte[]> byteslist) {
      if (byteslist == null) return null;
      List<T> list = new ArrayList<>();
      for (byte[] bytes : byteslist) {
            T t = toObj(bytes);
            list.add(t);
      }
      return list;
    }

    @SuppressWarnings("unchecked")
    public static <T> T toObj(byte[] bytes) {
      if (bytes == null || bytes.length < 8) return null;
      try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return (T)ois.readObject();
      } catch (IOException|ClassNotFoundException e) {
            throw new RuntimeException(e);
      }
    }
}在服务中的调用方式
@Autowired
private RedisTemplate<String, byte[]> redisBytesTemplate;

@Override
public Boolean hasKey(String key) {
    return redisBytesTemplate.hasKey(key);
}

@Override
public Boolean hashHasKey(String key, String field) {
    return redisBytesTemplate.opsForHash().hasKey(key,field);
}

@Override
public Integer hashGetInt(String key, String field) {
    HashOperations<String, String, byte[]> opsForHash = redisBytesTemplate.opsForHash();
    byte[] bytes = opsForHash.get(key, field);
    return bytes == null? null : ByteUtil.toInteger(bytes);
}

@Override
public void hashSetInt(String key, String field, int value) {
    HashOperations<String, String, byte[]> opsForHash = redisBytesTemplate.opsForHash();
    opsForHash.put(key, field, ByteUtil.toByte(value));
}

@Override
public <T> T hashGetObj(String key, String field) {
    HashOperations<String, String, byte[]> opsForHash = redisBytesTemplate.opsForHash();
    return ByteUtil.toObj(opsForHash.get(key, field));
}

@Override
public <T> void hashSetObj(String key, String field, T value) {
    HashOperations<String, String, byte[]> opsForHash = redisBytesTemplate.opsForHash();
    opsForHash.put(key, field, ByteUtil.toByte(value));
}

/**
* @param timeout seconds to block
*/
@Override
public <T> T bLPopObj(int timeout, String key) {
    ListOperations<String, byte[]> opsForList = redisBytesTemplate.opsForList();
    byte[] bytes = opsForList.leftPop(key, timeout, TimeUnit.SECONDS);
    return ByteUtil.toObj(bytes);
}

@Override
public <T> Long rPush(String key, T value) {
    ListOperations<String, byte[]> opsForList = redisBytesTemplate.opsForList();
    return opsForList.rightPush(key, ByteUtil.toByte(value));
}参考


[*]https://vincentbogousslavsky.com/post/configuration-for-spring-data-redis-reactive-for-connecting
创建 RedisClusterConfiguration
[*]https://blog.csdn.net/weixin_67601403/article/details/129706748
创建 RedisConnectionFactory lettuceConnectionFactory
[*]https://cloud.tencent.com/developer/article/2371793
默认的级联配置方式

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Spring Boot 3 配置 Redis 兼容单例和集群