Redis哨兵模式部署(sentinel)以及SpringBoot2.0整合Sentinel哨兵

不完美的主从复制

上一章中简单了实现了一个主从复制使的Redis实现了读写分离。通过实现主从复制使得数据安全有了保障,且读写分离提高了Redis的整体性能。但是Redis并不能完美解决主服务宕机后,整体服务的不可用。

上一章的例子中,假如主服务宕机后,我们需要手动把一台从服务器切换成主服务,这个动作全程需要人工干预。
中间可能发生操作失误,且在操作过程中会导致一段时间内服务不可用。为了解决上面的问题,我们需要引入一套自动化的解决方案,那就是哨兵机制。

哨兵模式

哨兵模式是Redis针对上述问题,提供的一套自动化解决方案的模式。其具体原理是配置哨兵通过命令从而监控多个Redis实例。通过命令请求,实时获取Redis服务其运行状态,以及其主从服务器的拓扑关系。当哨兵确认主服务宕机会根据选举将某个从服务切换成主服务,然后通过发布订阅模式通知其他从服务,修改配置,切换主机。

整个关系可以用下面的图描述

redis
哨兵
监控
监控
监控
复制
监控
监控
复制
哨兵2
主服务
从服务1
从服务2
哨兵1
哨兵3

监控流程

哨兵对数据的监控

  1. 哨兵通过向主服务发送info信息,获取到从服务的信息
  2. 每个哨兵会以10秒的频率向主服务和从服务发送info命令获取其最新的信息。
  3. 每隔一秒哨兵会向主服务、从服务和其余哨兵发送一次心跳检测

哨兵间的监控

  1. 每个哨兵会以每秒2秒的频率向Redis节点指定的频道上发送该哨兵对主服务的判断以及自己的信息
  2. 每个哨兵会通过发布订阅模式,获取其他哨兵信息以及对主服务节点的判断。

主服务宕机

  1. 当哨兵检测到主服务器宕机后,会标记此服务主观下线会向其他哨兵发送is-master-down-by-addr命令,需要其他哨兵协助判断该主服务状态。
  2. 当超过quorum(仲裁值)个哨兵确认主服务主观下线,则当前哨兵就将主服务实例标记为客观下线。
  3. 当然有超过quorum(仲裁值)数量的哨兵认为主服务客观下线,则其中一个就会尝试启动故障转移
  4. 如有有超过一半的哨兵可用的时候,故障转移将会实际启动。所以当故障期间没有超过一半的哨兵可用的情况下,故障转移永远不会启动。
  5. 在启动故障转移之后,哨兵之间会进行选举指定领导节点
  6. 当确定领导节点之后,会从主服务下的从服务中根据规则选择一个从服务升级为主服务
  7. 随后领导节点向剩下从服务发送“slaveof”命令,引导其他从服务切换主服务。
  8. 最后新的主服务,通过发布订阅将主服务信息广播给其他哨兵
未到达到仲裁值
达到仲裁值
标记客观下线的哨兵不足一半
标记客观下线的哨兵超过一半
哨兵1
发现主服务宕机
请求其他哨兵协助判断
等待确认或者取消主观下线
标记为客观下线,尝试启动故障转移
等待确认
启动故障转移
哨兵间选举领导节点
领导节点指定新的主服务
引导从服务切换至主服务

Sentinel never starts a failover if the majority of Sentinel processes are unable to talk

根据上面官方文档中的描述,在一半以上哨兵不可用的时候,故障转移不会启动。
所以这也是我们一般要求配置3个及以上的哨兵,只配置1个或2个哨兵,当哨兵宕机的时候,启动故障转移则无法启动。

配置哨兵模式

配置主从复制

这里介绍单机配置主从复制,一主两从

配置Redis客户端

将Redis客户端复制三分,分别为redis、redis-slave1、redis-slave2

修改两个从服务配置

  1. 需要修改位置,修改端口。两个从服务端口修改为6380、6381
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6380
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6381
  1. 配置主服务信息。因为主服务配置密码requirepass,所以从服务都需要配置主服务的密码(注意之前配置主从的时候并不会给主服务配置masterauth。但是为了方便主服务宕机后重新加入服务,所以主从都使用了一样的密码配置,而哨兵虽然会修改slaveof,但并不会修改masterauth,所以主服务需要添加masterauth
# 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
slaveof 127.0.0.1 6379
# 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
masterauth 123456
  1. 启动三个服务,分别进入三个不同Redis文件下的src目录。分别启动下面的命令
redis-server ../redis.conf
  1. 依次连接三个客户端(需要三个窗口)
redis-cli -p 6379

redis-cli -p 6380

redis-cli -p 6381

  1. 通过向6379的实例设置参数,验证主从复制是否完成。

配置哨兵

到这一步,我们赢已经有一个主二从的集群了。现在我们需要为这个主从复制的集群设置哨兵。
现在将Redis原始的客户端复制三分,配置3个哨兵,分别为redis-sentinel1、redis-sentinel2、redis-sentinel3。
每个哨兵需要修改各自的端口,以及监听的主服务器。在Redis安装目录下有一个sentinel.conf文件针对哨兵的配置修改在文件内。

每个哨兵配置不同的启动端口。

哨兵端口
redis-sentinel1port 26379
redis-sentinel2port 26380
redis-sentinel3port 26381

针对主服务的配置,每个哨兵的配置都是一样的。

# 配置监听的主服务器,monitor代表监控,mymaster代表服务器的名称,可以自定义,127.0.0.1代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123456

然后在哨兵各自src文件下执行下面的命令

redis-sentinel ../sentinel.conf

测试哨兵模式

可以看到三个数据节点以及三个哨兵已经启动

[root@name src]# ps aux|grep redis                      
root      8024  0.0  0.0 156452  7656 ?        Ssl  11:47   0:14 redis-server 127.0.0.1:6379
root      8029  0.0  0.0 156452  7944 ?        Ssl  11:47   0:14 redis-server 0.0.0.0:6380
root      8067  0.0  0.0 156452  7876 ?        Ssl  11:50   0:14 redis-server 0.0.0.0:6381
root      8072  0.0  0.0  24720  7472 pts/1    S+   11:51   0:00 redis-cli -p 6380
root      8074  0.0  0.0  24720  7476 pts/2    S+   11:52   0:00 redis-cli -p 6381
root      8443  0.1  0.0 153892  7604 ?        Ssl  16:20   0:00 redis-sentinel *:26379 [sentinel]
root      8448  0.1  0.0 153892  7584 ?        Ssl  16:21   0:00 redis-sentinel *:26380 [sentinel]
root      8461  0.0  0.0 153892  7876 ?        Ssl  16:23   0:00 redis-sentinel *:26381 [sentinel]

我们连接哨兵去查看现在的主服务

[root@name src]# redis-cli -p 26379
127.0.0.1:26379> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "6379"
 7) "runid"
 8) "f9ea22f2431b0e4773aa850db11a15cd20d0e169"
 9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "599"
19) "last-ping-reply"
20) "599"
21) "down-after-milliseconds"
22) "30000"
23) "info-refresh"
24) "3576"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "1288349"
29) "config-epoch"
30) "0"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"

模拟故障

我们直接kill掉主服务。

[root@name src]# ps aux|grep redis
root      8024  0.0  0.0 156452  7744 ?        Ssl  11:47   0:16 redis-server 127.0.0.1:6379
root      8029  0.0  0.1 156452  8196 ?        Ssl  11:47   0:15 redis-server 0.0.0.0:6380
root      8067  0.0  0.1 156452  8200 ?        Ssl  11:50   0:15 redis-server 0.0.0.0:6381
root      8072  0.0  0.0  24720  7472 pts/1    S+   11:51   0:00 redis-cli -p 6380
root      8074  0.0  0.0  24720  7476 pts/2    S+   11:52   0:00 redis-cli -p 6381
root      8443  0.1  0.0 153892  7872 ?        Ssl  16:20   0:02 redis-sentinel *:26379 [sentinel]
root      8448  0.1  0.0 153892  7896 ?        Ssl  16:21   0:01 redis-sentinel *:26380 [sentinel]
root      8461  0.1  0.0 153892  7892 ?        Ssl  16:23   0:01 redis-sentinel *:26381 [sentinel]
root      8503  0.0  0.0 112712   980 pts/0    S+   16:44   0:00 grep --color=auto redis
[root@name src]# kill -9 8024

此时可以看到主服务已经是6380了

[root@name src]# redis-cli -p 26379
127.0.0.1:26379> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "6380"
 7) "runid"
 8) "0405ebd58dc850ef5d59cf69cbe7c4d7a89f902b"
 9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "903"
19) "last-ping-reply"
20) "903"
21) "down-after-milliseconds"
22) "30000"
23) "info-refresh"
24) "9753"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "29924"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"

此时我们重启6379的实例,然后查看主从关系。会发现6379成为了6380的从服务

127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=336185,lag=1
slave1:ip=127.0.0.1,port=6379,state=online,offset=336185,lag=1
master_replid:6d702a86ec544bd86516218bf96903ec5b697e42
master_replid2:61c841bb0fc2c9b708032e39ae21eb4cc1f45ca8
master_repl_offset:336318
second_repl_offset:290704
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:336318

Spring Boot2.0使用Redis哨兵

这里我使用了我以前项目作为模板: redis简单使用

spring:
  application:
    name: sample.redis
  redis:
    # 使用哨兵模式这两个就可以不使用了
    #database: 0
    #host: 127.0.0.1
    jedis:
      pool:
        #最大连接数据库连接数,设 0 为没有限制
        max-active: 8
        #最大等待连接中的数量,设 0 为没有限制
        max-idle: 8
        #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
        max-wait: -1ms
        #最小等待连接中的数量,设 0 为没有限制
        min-idle: 0
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: -1ms
        min-idle: 0
      shutdown-timeout: 1000ms
    # 哨兵信息的配置
    sentinel:
      master: mymaster
      nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
    password: 123456

server:
  port: 8000

一个用来测试的Controller

package dai.samples.redis.pubsub.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author daify
 * @date 2019-09-01
 */
@RestController
@RequestMapping("test")
public class TestController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 
     * @return
     */
    @RequestMapping(value = "{key}/{value}",method = RequestMethod.GET)
    public String setData(@PathVariable("key") String key,
                        @PathVariable("value") String value) {
        stringRedisTemplate.opsForValue().set(key,value);

        String s = stringRedisTemplate.opsForValue().get(key);

        return s;
    }

}

我们请求: http://localhost:8000/test/one/31 ,可以拿到返回值31。

关闭主服务

现在我们在服务器中kill掉主服务

[root@name src]# ps aux|grep redis 
root      8741  0.1  0.0 156452  7696 ?        Ssl  18:24   0:03 redis-server 0.0.0.0:6379
root      8749  0.4  0.0 153892  7756 ?        Ssl  18:25   0:09 redis-sentinel 0.0.0.0:26379 [sentinel]
root      8759  0.4  0.0 153892  7812 ?        Ssl  18:25   0:09 redis-sentinel 0.0.0.0:26381 [sentinel]
root      8822  0.4  0.0 153892  7608 ?        Ssl  18:54   0:02 redis-sentinel 0.0.0.0:26380 [sentinel]
root      8835  0.0  0.0  24720  7476 pts/0    S+   18:55   0:00 redis-cli -p 26379
root      8857  0.2  0.1 162596  9500 ?        Ssl  19:00   0:00 redis-server 0.0.0.0:6381
root      8877  0.2  0.0 156452  7704 ?        Ssl  19:03   0:00 redis-server 0.0.0.0:6380
root      8882  0.0  0.0 112708   980 pts/1    S+   19:03   0:00 grep --color=auto redis
[root@name src]# kill -9 8741
[root@name src]# ps aux|grep redis 
root      8749  0.4  0.0 153892  7704 ?        Ssl  18:25   0:09 redis-sentinel 0.0.0.0:26379 [sentinel]
root      8759  0.4  0.0 153892  7696 ?        Ssl  18:25   0:09 redis-sentinel 0.0.0.0:26381 [sentinel]
root      8822  0.4  0.0 153892  7696 ?        Ssl  18:54   0:02 redis-sentinel 0.0.0.0:26380 [sentinel]
root      8835  0.0  0.0  24720  7476 pts/0    S+   18:55   0:00 redis-cli -p 26379
root      8857  0.2  0.1 162596  9616 ?        Ssl  19:00   0:00 redis-server 0.0.0.0:6381
root      8877  0.1  0.0 156452  7888 ?        Ssl  19:03   0:00 redis-server 0.0.0.0:6380
root      8885  0.0  0.0 112708   976 pts/1    S+   19:03   0:00 grep --color=auto redis

然后请求:http://localhost:8000/test/two/120 ,可以拿到120的返回值。而这个过程原来的主服务已经发生了变化,并且这个变化全程是自动化的。

可能出现的问题

  • 无法连接哨兵
Caused by: io.lettuce.core.RedisConnectionException: Cannot connect to a Redis Sentinel

本人使用的是阿里云,默认没有打开哨兵所选的端口,只需要在控制台中打开。


  • 出现127.0.0.1这种内网地址加端口

因为demo使用的单服务器多Redis实例。最开始使用127.0.0.1 +

Unable to connect to 127.0.0.1:6380

端口方式配置,而测试代码在本地导致获取的IP无法访问,这里需要修改主从配置和哨兵的配置

主从配置,修改为外网访问的地址

# 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
slaveof 127.0.0.1 6379
# 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
masterauth 123456

哨兵配置,修改为外网访问的地址

# 配置监听的主服务器,monitor代表监控,mymaster代表服务器的名称,可以自定义,127.0.0.1代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123456

NOAUTH Authentication required

io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required

简单的就是设置了密码但是没有提供与之匹配的密码设置。这里需要检查application.yml中的密码是否配置,虽然在哨兵中配置了服务的密码,但是在项目中密码的设置还是需要的。


找不到了slaveof

在发现配置错误之后尝试修改主从配置中的slaveof但是你可能无法找到之前配置的slaveof因为在Redis 5之后的版本中,因为slave有奴隶的含义,被相关人士抗议,作者为了避免奴隶制相关描述。后续修改了slave相关的参数,在启动后对配置重写的时候会将slaveof替换为replicaof

replicaof 127.0.0.1 6380
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值