Redis集群搭建
[!tip]
本文主要讲解Redis集群搭建以及在Java中如何与Spring Boot项目进行整合。
同时,因为涉及到集群,要起多个Redis实例,我没有这么多机器,所以就统一用的云应用做的。
我使用的云应用是雨云这家的,点击此处推广链接直达,且有优惠。
在正式开始之前,Redis的集群搭建有多种方式,你可以是同一内网下的,也可以是跨网的,可以是直接使用二进制包的方式搭建,也可以直接使用Docker搭建。
我这里是直接使用的云应用(k8s+Docker)来完成的。这里无论那种方式,核心原理都是一样的。
1. Redis配置文件
当你把Redis安装好了之后,再正式启动之前,你需要修改redis.conf文件。
核心修改的配置如下:
# 让任意IP可以连接本Redis
bind 0.0.0.0
# 禁用保护模式 根据bind配置视情况而定,不强制要求
protected-mode no
# redis是否以守护进程运行,注意,如果你的redis是docker启动的,那么这里必须是no
daemonize yes
# 配置redis的连接密码,不强求配置,但是最后还是配上
requirepass 你的redis密码
# 配置redis集群密码,当从节点连接主节点的时候,需要使用到此密码,所以建议不论是从节点还是主节点,都配置上,且密码保持一致
# 个人建议此配置需要强制配置
masterauth redis集群密码
# 开启集群模式,强制要求
cluster-enabled yes
# 指定集群节点配置文件露肩,强制要求,注意,这里的文件不能重名,也就是说如果你是同一台机器启动的多个redis实例,
# 那么这里可能需要更改
cluster-config-file nodes-6379.conf
# 定义集群节点间通信的超时时间。如果主节点挂了,那么从节点在超过这个时间之后仍然无法接受到主节点的数据,
# 那么就会判断主节点已经失效了,此时从节点有可能会被选举为新的主节点
cluster-node-timeout 15000
# 由于你的redis要配置为集群,在集群模式下,只有1个数据库,所以配置为1
# 此处不强制要求,如果没配置,redis日志中只是有warning日志
databases 1
# 在复杂网络环境(如Docker、NAT、云服务器、负载均衡后)中正确暴露集群节点地址,
# 这里建议配置上,否则后面java程序连接的时候,他会默认走内网IP,你不一定能够连上
cluster-announce-ip IP地址(公网/内网)
# 节点对外宣告的客户端端口,一般和redis客户端的端口保持一致
cluster-announce-port 6379
# 节点对外宣告的集群总线端口,一般是redis客户端的端口+10000
cluster-announce-bus-port 16379
[!important]
对于redis的端口和集群总线端口最好要保证各个redis能够访问,也就是防火墙你得放过这些端口,因为搭建集群的时候,这两个端口都是必须的。
我这里是云应用(k8s+Docker),所以我的这里的需要的配置如下:
bind 0.0.0.0
protected-mode no
daemonize no
requirepass 1q2w#E
masterauth 1q2w#E
cluster-enabled yes
# 由于是docker启动的,每个docker启动一个redis,所以我这里不需要改这里的文件名字,不会出现冲突
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
databases 1
cluster-announce-ip 公网IP
cluster-announce-port 6379
cluster-announce-bus-port 16379
2. 端口开放(可选)
要保证集群内能够顺利通信,必须开放集群总线端口,同时,如果你要外部可访问你的redis,你还要把redis的客户端端口给开放出来,也就是常说的6379(根据实际开放)。
我这里是云应用(k8s+Docker),我要配置k8s的service将这两个端口开放出来,如图:

[!note]
不论你是以何种方式开放端口,只要开放出来就行了。
3. Redis集群搭建
3.1 主节点搭建
配置文件以及端口都准备好了之后,现在我们就可以搭建集群了,对于Redis集群,至少要有3个节点才可搭建,如果你只有2个Redis节点,那么只能搭建主从,不能搭建集群。
所以,我这里准备3个Redis,3个Redis的配置文件都使用上述的配置文件,使用命令搭建Redis集群:
redis-cli -a redis密码 --cluster create \
redis主节点地址(IP):客户端端口 \
redis主节点地址(IP):客户端端口 \
redis主节点地址(IP):客户端端口 \
... \ # 其余多个master节点
--cluster-replicas 0
对应到我这里,我的命令就是如下:
redis-cli -a "1q2w#E" --cluster create \
svc-redis-wkii6w:6379 \
svc-redis-7ddieo:6379 \
svc-redis-cnmlth:6379 \
--cluster-replicas 0
[!tip]
由于我这里是k8s,所以我使用的是service+端口的方式访问,这样在k8s中,多个Pod在内网就能够互相通信了。


[!note]
如果在执行上述命令的时候,在执行过程中,如果一直卡在
Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join。这一步,那么基本上就是你的集群总线端口没有开放出来。
当上述命令执行成功之后,3个master节点就搭建好了。
在任意节点执行如下命令查看验证:
redis-cli -a "1q2w#E" cluster nodes

3.2 从节点加入到主节点中
之前构建了集群的主节点(Masrer节点),如果你还要搭建从节点(Slave节点),并且把从节点也加入到集群中,那么这时候就构成了主从集群,容灾效果更好,并且从节点负责读,主节点负责写,读写也是分离的,性能更好。
对于从节点的配置文件更改,你参考主节点的配置文件就可以了,唯一要注意的是必须要配置masterauth,这样从节点才能够连接上主节点,从而进行通信。
同理,从节点也是需要开放客户端端口和集群总线端口,然后通过命令将改节点作为从节点加入到主节点中:
redis-cli -a redis密码 --cluster add-node 从节点地址(IP):客户端端口 主节点地址(IP):客户端端口 --cluster-slave
对应到我这里就是:
redis-cli -a 1q2w#E --cluster add-node svc-redis-0nabc7:6379 svc-redis-wkii6w:6379 --cluster-slave
我的svc-redis-0nabc7就是我的从节点Redis的地址,我要把这个Redis节点当作从节点加入到主节点svc-redis-wkii6w中,从而构成主从结构,这里需要在对应的主节点上面执行该命令:


3.3 连接工具连接集群(可选)
当你的集群搭建好了之后,你就可以使用Redis可视化工具连接集群,我这里的使用AnotherRedisDesktopManager作为连接工具。

4. SpringBoot整合Redis集群与Redisson
添加必要依赖:
<!--Redis与SpringBoot的整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Redisson与SpringBoot的整合依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.52.0</version>
</dependency>
编写配置文件:
server:
port: 12344
spring:
application:
name: redis-cluster-demo
redis:
# Redis 集群配置
cluster:
nodes:
- 114.66.61.131:6379
- 114.66.61.130:6379
- 110.42.45.237:6379
- 110.42.45.236:6379
max-redirects: 3 # 最大重定向次数
password: 1q2w#E # 如果有密码
connect-timeout: 3s # 连接超时时间
timeout: 3s # 读取超时时间
# Lettuce 连接池配置
lettuce:
pool:
max-active: 200 # 连接池最大连接数
max-idle: 10 # 连接池最大空闲连接数
min-idle: 0 # 连接池最小空闲连接数
max-wait: -1ms # 连接池最大阻塞等待时间
cluster:
refresh:
period: 30s # 集群拓扑刷新周期
adaptive: true # 自适应刷新策略
编写Redis与Redisson的配置类。
Redis配置类:
package cn.yunrain.redisclusterdemo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Author 念心卓
* Date 2025/12/2
* Description Redis配置类
*/
@Configuration
@EnableCaching // 启用缓存支持,后续可以使用Spring Cache注解
public class RedisConfig {
/**
* 自定义RedisTemplate,设置序列化方式,避免存储乱码
*
* @param redisConnectionFactory Redis连接工厂
* @return 自定义的RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
// 设置可见性和类型信息,针对所有属性(字段、getter、setter等),允许所有访问级别的属性都被序列化(包括private私有字段)解决反序列化时的类型丢失问题
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 启用默认的类型信息,在JSON中添加类型信息(如"@class":"com.example.User"),这样反序列化时才能知道要将JSON还原成什么类型的Java对象
// 注意:使用LaissezFaireSubTypeValidator来避免安全风险,DefaultTyping.NON_FINAL:为非final类型(绝大多数类)添加类型信息
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
// 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key 值
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// key 采用 String 的序列化方式
template.setKeySerializer(stringSerializer);
// hash 的 key 也采用 String 的序列化方式
template.setHashKeySerializer(stringSerializer);
// value 序列化方式采用 jackson
template.setValueSerializer(serializer);
// hash 的 value 序列化方式采用 jackson
template.setHashValueSerializer(serializer);
// 初始化Template,检查必要属性是否已设置。这个方法由Spring自动调用,这里显式调用确保立即初始化。
template.afterPropertiesSet();
return template;
}
}
Redisson配置类:
package cn.yunrain.redisclusterdemo.config;
import cn.yunrain.redisclusterdemo.config.properties.RedisProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* Author 念心卓
* Date 2025/12/2
* Description Redisson配置类(集群模式)
*/
@Configuration
public class RedissonConfig {
@Resource
private RedisProperties redisProperties;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
String[] clusterNodes = redisProperties.getCluster().getNodes();
// 配置集群模式
String[] nodes = new String[clusterNodes.length];
for (int i = 0; i < clusterNodes.length; i++) {
nodes[i] = "redis://" + clusterNodes[i];
}
config.useClusterServers() // 使用集群服务器模式
.addNodeAddress(nodes) // 添加集群节点地址
.setPassword(redisProperties.getPassword().isEmpty() ? null : redisProperties.getPassword()) // 设置密码,如果为空则不设置
.setScanInterval(2000) // 集群扫描间隔时间
.setConnectTimeout(3000) // 连接超时
.setTimeout(3000) // 命令等待超时
.setRetryAttempts(3) // 命令失败重试次数
.setRetryInterval(1500) // 命令重试间隔
.setMasterConnectionPoolSize(64) // 主节点连接池大小,针对写操作
.setSlaveConnectionPoolSize(64) // 从节点连接池大小,针对读操作
.setIdleConnectionTimeout(10000); // 连接空闲超时,超过此时间未使用的连接将被关闭
// 设置编码,使用JsonJacksonCodec进行序列化和反序列化
config.setCodec(new JsonJacksonCodec());
// 设置线程数
config.setThreads(16);
config.setNettyThreads(32);
// 设置传输模式为NIO
config.setTransportMode(org.redisson.config.TransportMode.NIO);
return Redisson.create(config);
}
}
由于spring.redis.cluster.nodes的配置值是一个数组,所以,我这里单独写了一个RedisProperties类,来获取数组:
package cn.yunrain.redisclusterdemo.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Author 念心卓
* Date 2025/12/2
* Description Redis配置
*/
@ConfigurationProperties(prefix = "spring.redis")
@Data
@Component
public class RedisProperties {
private Cluster cluster;
private String password;
@Data
public static class Cluster {
private String[] nodes;
private Integer maxRedirects;
}
}
现在启动程序验证是否能够成功启动:

[!caution]
如果你的Redis和我一样是Docker/Nat/k8s/云应用搭建的,并且此处发送是内网IP,同时,你程序也连接不上,那么你就需要检查一下你每个Redis的redis.conf配置文件中,对应的
cluster-announce-xxx配置是否正确。
Comments