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的初始化.

java 复制代码
@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[strs.length][];
        for (int i = 0; i < strs.length; i++) {
            arr[i] = strs[i].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);
        }
    }
}

在服务中的调用方式

java 复制代码
@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));
}

参考

相关推荐
别惹CC11 分钟前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
柯南二号1 小时前
【Java后端】Spring Boot 集成 MyBatis-Plus 全攻略
java·spring boot·mybatis
javachen__2 小时前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
IT毕设实战小研9 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
一只爱撸猫的程序猿10 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋10 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
鼠鼠我捏,要死了捏10 小时前
生产环境Redis缓存穿透与雪崩防护性能优化实战指南
redis·cache
武昌库里写JAVA12 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习
Pitayafruit13 小时前
Spring AI 进阶之路03:集成RAG构建高效知识库
spring boot·后端·llm
zru_960213 小时前
Spring Boot 单元测试:@SpyBean 使用教程
spring boot·单元测试·log4j