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

参考

posted on   Milton  阅读(484)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
历史上的今天:
2017-10-09 Redis常用命令
2015-10-09 MySQL迁移[转]

导航

< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8
点击右上角即可分享
微信分享提示