Redis 主从+哨兵 环境搭建

说明:记录Redis主从结构集群搭建,伪集群示例 ,所有的Redis节点都在一个虚拟机上,所以采用不同端口号的形式,使用不同的配置文件

集群结构如下:

image

0、安装Redis

下载Redis:https://redis.io/download/

image

将下载的 redis-6.2.6.tar.gz 压缩包,上传到Linux,解压缩,假设上传到/opt/app目录下

[root@56 app]# tar -zxvf redis-6.2.6.tar.gz

检查是否安装了 gcc编译环境,如果没有安装,使用yum安装

# 检查是否安装了gcc
[root@56 redis-6.2.6]# gcc -v
# 如果没有安装,使用yum安装
[root@56 redis-6.2.6]# gcc yum install gcc

编译Redis

[root@56 redis-6.2.6]# cd /opt/app/redis-6.2.6
[root@56 redis-6.2.6]# make

之后,redis的脚本,会生成在此目录下:/opt/app/redis-6.2.6/src

测试:
image

1、配置文件

将Redis配置文件复制为三份,命名分别为:conf6379.conf、conf6380.conf、conf6381.conf

修改以下配置,将每个节点的配置文件,以下配置中的所有的6379,替换为此节点的端口号(如果集群不在一个机器上则忽略),这样做是为了不让生成的RDB等数据文件名字冲突和端口冲突。

修改配置文件中的以下配置:

# 将bind这一行注释掉,或者修改为0:0:0:0,这表示任意地址都可以连接此Redis服务
# bind 127.0.0.1
# 关闭保护模式,如果开启的话,外部客户端就连不上Redis
protected-mode no
# 配置redis的端口号,不同节点使用不同的端口号
port 6379
# 以守护进程运行(后台运行redis)
daemonize yes
# 服务启动后记录线程号的文件(不同节点,将下面的6379替换为此节点的端口号,防止冲突)
pidfile "/var/run/redis_6379.pid"
# 日志文件名字 (不同节点,将下面的6379替换为此节点的端口号,防止冲突)
logfile "log6379.log"
# 数据库的个数
databases 16
# 设置数据保存到数据文件中的save规则,3600秒内修改1次key,进行一次磁盘保存操作
save 3600 1
save 300 100
save 60 10000
# 指定存储至本地数据库时是否压缩数据,默认是yes,redis采用LZF压缩,需要消耗CPU资源
rdbcompression yes
# 保存rdb文件时,是否对rdb文件进行校验
rdbchecksum yes
# 保存数据的文件名字 (不同节点,将下面的6379替换为此节点的端口号,防止冲突)
dbfilename "dump6379.rdb"
# 保存数据的目录,这个目录需要提前创建出来(不同节点,将下面的6379替换为此节点的端口号,防止冲突)
dir "/opt/apps/redis/data6379"
# 是否开启aof持久化
appendonly yes
# aof文件名字 (不同节点,将下面的6379替换为此节点的端口号,防止冲突)
appendfilename "appendonly6379.aof"
# 集群配置文件,自动生成,不能人为维护 (不同节点,将下面的6379替换为此节点的端口号,防止冲突)
cluster-config-file "nodes-6379.conf"

再将次配置复制两份,作为节点6380、6381的配置文件

2、配置集群

启动redis服务

./redis-server conf6379.conf
./redis-server conf6380.conf
./redis-server conf6381.conf

启动之后,查询redis进程:

image

将6380、6381作为6379的从节点:

# 配置6380作为6379的从节点
[root@localhost redis]# ./redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6379
# 配置6381作为6379的从节点
[root@localhost redis]# ./redis-cli -p 6381
127.0.0.1:6381> slaveof 127.0.0.1 6379

然后在主节点或者从节点上执行info replication命令可以查看状态,出现以下信息即表示成功:

主节点查看:

image

从节点查看:

image

主从断连:

让从节点与主节点断开连接,如果断开连接,此时,从节点已变为独立节点,当前从节点的数据和主节点的数据是一模一样的。

# 在从节点上执行
127.0.0.1:6380> slaveof no one
OK

3、搭建哨兵(Sentinel)节点

创建一个哨兵配置文件 sentinel.conf ,加入以下内容

# redis哨兵配置文件
# 配置监听的主节点,sentinel monitor代表监控,myredis代表服务名称(可以自定义)
# 192.168.2.56 6379 分别是主节点的IP和端口,
# 注意如果哨兵和主节点在一台服务器上,这里IP不能使用127.0.0.1,而是要用 192.168.2.56的这种
# 1代表有一个或一个以上哨兵认为主节点不可用时,进行选举操作
sentinel monitor myredis 192.168.2.56 6379 1
# 关闭保护模式,只有关闭之后,才能远程连接
protected-mode no
# 哨兵端口
port 26379
# 哨兵数据存放位置
dir "/opt/app/redis-6.2.6/sentinel-data-26379"
# 日志文件
logfile "../sentinel-data-26379/log-sentinel-26379.log"
# 以守护进程运行
daemonize yes
# 进程ID保存位置
pidfile "/var/run/sentinel-data-26379.pid"

注意这里有一个坑:

在上面的哨兵配置文件 sentinel.conf 里面配置 master 节点IP时,不要使用127.0.0.1,要用具体的IP(192.168.2.56这种),如果使用前一种,使用Java客户端连接,从 JedisSentinelPool 获取 Jedis jedis = sentinelPool.getResource(); 会报出以下异常:

redis.clients.jedis.exceptions.JedisConnectionException: Failed to connect to any host resolved for DNS name.

image

启动哨兵:

./redis-sentinel sentinel.conf

查看进程,结果如下:

[root@localhost redis]# ps -ef|grep redis
root 2314 1 0 20:56 ? 00:00:06 ../bin/redis-server *:6379
root 2461 1 0 21:01 ? 00:00:05 ../bin/redis-server *:6380
root 2468 1 0 21:01 ? 00:00:05 ../bin/redis-server *:6381
root 3023 1 0 21:23 ? 00:00:05 ../bin/redis-sentinel *:26379 [sentinel]
root 3061 1399 0 22:16 pts/0 00:00:00 grep --color=auto redis

此时,如果 6379 主节点挂了,哨兵节点会选择新的主节点。

测试:将主节点shutdown,稍等几秒,哨兵会在从节点中重新选择新的主节点:

原主节点:

image

哨兵 :

image

新主节点:

image

redis6381 节点被选为新的主节点,如果redis6379再次连接上,那他也是redis6381的从节点;

4、Java客户端连接

单机Redis,Java客户端配置

新建一个maven项目,在pom文件中,引入jedis依赖:

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.1.1</version>
</dependency>

建一个类测试:

public class Test {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.2.56", 6379);
String a = jedis.set("a", "1");
System.out.println(a);
System.out.println(jedis.get("a"));
jedis.close();
}
}

Redis主从复制+哨兵模式,Java客户端的配置

仍然是在pom文件中,引入jedis依赖:

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.1.1</version>
</dependency>

这种模式,只需要配置所有哨兵的IP和端口,放在Set集合内,新建一个类测试:

public class Test {
public static void main(String[] args) {
// 哨兵节点
HashSet<String> sentinelSet = new HashSet<>();
sentinelSet.add("192.168.2.56:26379");
// 创建pool
JedisSentinelPool sentinelPool = new JedisSentinelPool("myredis", sentinelSet);
System.out.printf("当前Master节点:%s%n", sentinelPool.getCurrentHostMaster());
// 从pool中,获取一个jedis对象,操作redis
Jedis jedis = sentinelPool.getResource();
System.out.printf("设置值:%s%n", jedis.set("a", "1"));
System.out.printf("获取值:%s%n", jedis.get("a"));
// 释放jedis到pool中
sentinelPool.returnResource(jedis);
// 释放pool
sentinelPool.close();
}
}

SpringBoot项目中,集成Redis

新建一个SpringBoot项目,pom文件中,加入以下依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot项目pom文件中,加入redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.6.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- fastjson,设置序列化-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>

在配置文件 application.properties 中,加入此 Redis哨兵的配置:

#哨兵节点IP和端口,多个之间用英文逗号分割
spring.redis.sentinel.nodes=191.168.2.56:26379
spring.redis.sentinel.master=myredis

创建一个java类,配置 RedisTemplate 的序列化方式:

@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

此时,Redis已经配置好了,如果要使用Redis,在对应的地方,注入 RedisTemplate 即可。

创建一个测试类:

// RedisBootApplication是启动类名字
@SpringBootTest(classes = RedisBootApplication.class)
class TestSer {
@Autowired
RedisTemplate redisTemplate;
@Test
void getString() {
redisTemplate.opsForValue().set("a", 100);
Object a = redisTemplate.opsForValue().get("a");
System.out.println(a);
}
}

5、问题

  • 为什么哨兵的配置文件,只需要配置主节点IP和端口?

    答:哨兵会从redis主节点上,获取从节点的连接信息,然后对其进行监听


posted @   周星星、同学  阅读(432)  评论(1编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示