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[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); } } }
在服务中的调用方式
@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
默认的级联配置方式
分类:
Java
标签:
Redis
, SpringBoot
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
2017-10-09 Redis常用命令
2015-10-09 MySQL迁移[转]