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

参考

相关推荐
luoluoal3 分钟前
基于Spring Boot的装饰工程管理系统源码(springboot)
java·spring boot·后端
2401_857617624 分钟前
Spring Boot框架在中小企业设备管理中的创新应用
数据库·spring boot·后端
涛涛6号29 分钟前
PageHelper(springboot,mybatis)
java·spring boot·后端
林戈的IT生涯2 小时前
SpringBoot中yaml配置文件中文字符异常以及将多个独立的IDEA项目整合到一个项目里当做模块的处理
java·spring boot·yaml配置文件·yml中文字符异常·idea项目合并
xrl20122 小时前
腾讯云或阿里云centos7安装Redis,并解决端口无法访问的问题
redis·阿里云·腾讯云
计算机学姐3 小时前
基于SpringBoot的汽车票网上预订系统
java·vue.js·spring boot·后端·mysql·java-ee·mybatis
哎呦没3 小时前
农村扶贫管理:SpringBoot解决方案
java·spring boot·后端
Karoku0664 小时前
【缓存与加速技术实践】Redis 高可用
运维·服务器·数据库·redis·mysql·缓存
Karoku0664 小时前
【缓存与加速技术实践】Redis 主从复制
linux·运维·服务器·数据库·redis·缓存
尘浮生5 小时前
Java项目实战II基于Spring Boot的火锅店管理系统设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·微信小程序·旅游