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 @ 2021-12-31 22:18  周星星、同学  阅读(395)  评论(1编辑  收藏  举报