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));
}

参考

相关推荐
!!!52529 分钟前
日志技术-LogBack入门程序&Log配置文件&日志级别
spring boot
feilieren3 小时前
SpringBoot 搭建 SSE
java·spring boot·spring
方圆想当图灵5 小时前
缓存之美:万文详解 Caffeine 实现原理(下)
java·redis·缓存
栗豆包5 小时前
w175基于springboot的图书管理系统的设计与实现
java·spring boot·后端·spring·tomcat
m0_748239477 小时前
springBoot发布https服务及调用
spring boot·后端·https
计算机-秋大田8 小时前
基于SpringBoot的高校教师科研的设计与实现(源码+SQL脚本+LW+部署讲解等)
java·vue.js·spring boot·后端·课程设计
web150850966418 小时前
Spring Boot整合WebSocket
spring boot·后端·websocket
LuckyRich18 小时前
2024年博客之星主题创作|2024年度感想与新技术Redis学习
数据库·redis·缓存
m0_748238279 小时前
SpringBoot最佳实践之 - 使用AOP记录操作日志
java·spring boot·后端
Q_274378510910 小时前
springboot基于微信小程序的健康管理系统
spring boot·后端·微信小程序