Redis哨兵模式

415 阅读16分钟

痛点

Redis主从复制模式已经实现了数据的多机备份,以及对于读操作的负载均衡。 但是有个主从复制有个致命的缺陷,一旦主节点由于故障不能继续提供服务,需要手动选定一个从节点晋升为主节点,同时还要更改其他从节点的配置指向新的主节点。Redis 2.8正式推出了哨兵模式实现了自动故障恢复。

哨兵功能

Redis SentinelRedis 高可用 的实现方案。Sentinel 是一个管理多个 Redis 实例的工具,它可以实现对 Redis监控通知自动故障转移。下面先对 Redis Sentinel基本概念 进行简单的介绍。

监控:Sentinel会不断检查主实例和副本实例的工作状态。

通知:当被监控的实例出现问题时,Sentinel可以通过API通知系统管理员或其他计算机程序

自动故障转移:主节点出现问题时,Sentinel会开始一次自动故障转移,Sentinel集群会协商选定一个从节点升级为主节点,并将其他的从节点指向新的主节点。

配置提供者:在Sentinel模式下,Sentinel充当客户端服务发现的权威来源,提供主节点的信息

img
img

概念介绍

为了更加清楚Sentinel模式是如何工作的,需要先了解以下的概念。

sentinel和salve自动发现机制

sentinel仅需要配置监测的master而不需要配置slaves,sentinel会自动的向master查询slave信息。

sentinel会利用redis的发布订阅功能来实现自动发现。所有的sentinel都会连接到所有master和slave的__sentinel__:hello频道,每两秒钟发布一条带有ip、port、runid的消息。所有sentinel都会收到这个消息,这个消息携带主节点的配置信息,如果自身保存的master配置信息旧于收到的,则立即更新。如果收到的sentinel hello消息携带的runid或地址信息已经存在,则删除自身保存的旧的sentinel,替换为新的这个。

主观下线和客观下线

默认情况下,Sentinel节点会以两秒一次的频率对Redis实例和其他Sentinel实例发送PING命令,并通过实例的回复判断实例的状态。

主观下线(SDOWN)

如果在down-after-millseconds毫秒内,Sentinel实例没有收到目标节点的有效回复,则判定该目标节点主观下线。

有效回复包括:+ PONG 、-LOADING错误、 -MASTERDOWN错误

客观下线(ODWON)

仅仅只有主观下线是不足以触发故障转移,所以对于主节点,还引入了一个客观下线的概念.

如果 主节点 出现故障,Sentinel 节点会通过 sentinel is-master-down-by-addr 命令,向其它 Sentinel 节点询问对该节点的 状态判断。如果超过 <quorum> 个数的节点判定 主节点 不可达,则该 Sentinel 节点会判断 主节点客观下线

从节点的选择

好了,现在Sentinel集群任务主节点客观下线了,选择哪一个从节点作为新的主节点呢。

Sentinel集群主要有以下评判标准,并且优先级从上往下:

  1. 从节点与主节点断开连接的时间
  2. 从节点的优先级
  3. 从节点从主节点接收数据量
  4. 节点的Run ID

首先,如果从节点与主节点断开连接的时间超过了(down-after-milliseconds * 10) + 主节点ODOWN时间,这样的从节点会被忽略,不会参与主节点的竞选。

从节点的配置文件中(redis.conf)中的配置项slave-priority(默认为100)声明了从节点的优先级,数值越低代表优先级越高.

slave-priority 值为 0,表示该从节点永远不会被推选主节点

如果多个从节点的优先级相同,则会检测Replication offset ,代表从节点从主节点接收数据量,Replication offset数值越大优先级越高。

最后,如果优先级和Replication offset都相同,那么就要看Run ID了,Run ID越小,优先级越高。

配置纪元(epochs)

就像古代皇帝的年号一样,更换主节点时,Sentinel实例会认为进入一个新纪元(epochs)。

当一个sentinel被授权进行故障迁移时,他会获得一个configuration epoch,用于标记配置信息的版本。——每一次故障迁移所使用的配置信息都对应一个配置纪元。

故障迁移还有一个规则:其他sentinel会等待被授权的sentinel failover-timeout的时间进行故障迁移,之后自己才会尝试failover。这保证同一时间只有一个sentinel在进行故障迁移。

哨兵配置

下面来说明一下常用的哨兵模式的配置。默认的哨兵模式配置文件叫做sentinel.conf.

  • sentinel monitor

规定了sentinel节点监控的 主节点的配置信息,quorum译为法定人数,超过quorum数量Sentinel节点认为主观下线时,才能认为主节点客观下线。

  • sentinel down-after-milliseconds

规定了节点在down-after-milliseconds未回复消息,就会被认为客观下线,默认30秒

  • sentinel failover-timeout

规定了,Sentinel节点进行故障转移的时间,默认三分钟。

  • sentinel parallel-syncs

指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。

  • logfile

指定该哨兵节点日志位置

  • port

指定该哨兵节点占用端口

  • daemonize

指定该哨兵节点是否为守护程序

至少需要三个哨兵实例

先来说明一下,哨兵模式的Redis实例最小要求是一主一从,同时最少需要三个哨兵实例。这可以用TCP连接为什么要三次握手用同样的思路回答------为什么两个哨兵不行。

先来说明一下故障转移的流程,当Sentinel集群认为主节点客观下线时,进行故障转移需要Sentinel集群中的大多数节点协商选出新的主节点和执行故障转移的Sentinel节点

如果,此时只有两个Sentinel节点,并且其中一个Sentinel节点和主节点位于同一台机器,此时机器挂掉了,那么,就会只剩下一个Sentinel节点。只有一个节点无法满足大多数的概念。从而无法进行故障转移。

img
img

Sentinel集群搭建

[Redis主从复制模式]()文章中已经讲述了如何搭建一个Redis主从实例。此处不在赘述。

现在,需要先修改sentinel.conf配置文件

port 16380
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000  
sentinel parallel-syncs mymaster 1  
# 如果节点需要密码的话
sentinel auth-pass mymaster 123456 
sentinel failover-timeout mymaster 180000
logfile "/usr/local/redis-4.0.11/log/sentinel-16380.log"
daemonize yes

接着,修改文件名,并复制两份相同的命令

mv     ./sentinel.conf   ./sentinel_16380.conf
cp     ./sentinel_16380.conf     ./sentinel_26380.conf
cp     ./sentinel_26380.conf     ./sentinel_36380.conf

sentinel_26380.conf的配置如下

port 26380
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000  
sentinel parallel-syncs mymaster 1  
# 如果节点需要密码的话
sentinel auth-pass mymaster 123456 
sentinel failover-timeout mymaster 180000
logfile "/usr/local/redis-4.0.11/log/sentinel-26380.log"
daemonize yes

sentinel_36380.conf的配置如下

port 36380
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000  
sentinel parallel-syncs mymaster 1  
# 如果节点需要密码的话
sentinel auth-pass mymaster 123456 
sentinel failover-timeout mymaster 180000
logfile "/usr/local/redis-4.0.11/log/sentinel-36380.log"
daemonize yes

在Redis主从实例启动后,用下面的命令,启动Sentinel集群

redis-sentinel ./sentinel-16380.conf
redis-sentinel ./sentinel-26380.conf
redis-sentinel ./sentinel-36380.conf

netstat -nltp命令查看端口情况

[root@localhost script]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:26379           0.0.0.0:*               LISTEN      7752/redis-server 0 
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      7742/redis-server 0 
tcp        0      0 0.0.0.0:26380           0.0.0.0:*               LISTEN      7764/redis-sentinel 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      7033/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      7316/master         
tcp        0      0 0.0.0.0:16379           0.0.0.0:*               LISTEN      7747/redis-server 0 
tcp        0      0 0.0.0.0:36380           0.0.0.0:*               LISTEN      7769/redis-sentinel 
tcp        0      0 0.0.0.0:16380           0.0.0.0:*               LISTEN      7759/redis-sentinel 
tcp6       0      0 :::22                   :::*                    LISTEN      7033/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      7316/master 

可以看到,对应的Redis节点和哨兵节点都已经被启动。

自动故障转移验证

ps -ef|grep redis命令 查看 与相关Redis进程状态

[root@localhost script]# ps -ef|grep redis
root       7742      1  0 01:50 ?        00:00:08 redis-server 0.0.0.0:6379
root       7747      1  0 01:50 ?        00:00:04 redis-server 0.0.0.0:16379
root       7752      1  0 01:50 ?        00:00:05 redis-server 0.0.0.0:26379
root       7759      1  0 01:50 ?        00:00:06 redis-sentinel 0.0.0.0:16380 [sentinel]
root       7764      1  0 01:50 ?        00:00:06 redis-sentinel 0.0.0.0:26380 [sentinel]
root       7769      1  0 01:50 ?        00:00:06 redis-sentinel 0.0.0.0:36380 [sentinel]
root       7839   7566  0 02:21 pts/0    00:00:00 grep --color=auto redis

可以看到,master节点的pid是7742,现在用kill -9 7742命令,模拟主节点发送了故障。

然后查看,哨兵节点的日志

[root@localhost script]# cat ../log/sentinel-16380.log 
21050:X 01 Jul 16:28:29.882 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
21050:X 01 Jul 16:28:29.882 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=21050, just started
21050:X 01 Jul 16:28:29.882 # Configuration loaded
21051:X 01 Jul 16:28:29.894 * Increased maximum number of open files to 10032 (it was originally set to 1024).
21051:X 01 Jul 16:28:29.896 * Running mode=sentinel, port=16380.
21051:X 01 Jul 16:28:29.897 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.

// 此哨兵节点的RUN ID
21051:X 01 Jul 16:28:29.920 # Sentinel ID is 20e0390dc2bcfc1f9edbf441aa0d658a67872f2a

// 哨兵节点启动之初6379作为主节点,并且通过主节点发现了两个slave节点 16379 26379 
21051:X 01 Jul 16:28:29.920 # +monitor master mymaster 127.0.0.1 6379 quorum 2
21051:X 01 Jul 16:28:29.926 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 16:28:29.931 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379

// 哨兵节点发现了另外的两个哨兵节点 26380 36380
21051:X 01 Jul 16:28:31.910 * +sentinel sentinel cb19acb18a984bd016f47c8c61c13a5b5c7e112d 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 16:28:31.935 * +sentinel sentinel 7d011dd04392288cdf5cde9b3f5ec8fee9d8dcd5 127.0.0.1 36380 @ mymaster 127.0.0.1 6379

// 哨兵节点认为6379主观下线
21051:X 01 Jul 17:33:12.842 # +sdown master mymaster 127.0.0.1 6379

// 哨兵集群认为6379客观下线
21051:X 01 Jul 17:33:12.909 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2

// 进入新纪元 开始进行故障转移
21051:X 01 Jul 17:33:12.909 # +new-epoch 1
21051:X 01 Jul 17:33:12.909 # +try-failover master mymaster 127.0.0.1 6379

// 因为此哨兵节点的RUN ID最小,从而成为故障转移的执行哨兵节点
21051:X 01 Jul 17:33:12.911 # +vote-for-leader 20e0390dc2bcfc1f9edbf441aa0d658a67872f2a 1
21051:X 01 Jul 17:33:12.916 # 7d011dd04392288cdf5cde9b3f5ec8fee9d8dcd5 voted for 20e0390dc2bcfc1f9edbf441aa0d658a67872f2a 1
21051:X 01 Jul 17:33:12.916 # cb19acb18a984bd016f47c8c61c13a5b5c7e112d voted for 20e0390dc2bcfc1f9edbf441aa0d658a67872f2a 1

// 选举26379Redis节点成为新的主节点
21051:X 01 Jul 17:33:12.988 # +elected-leader master mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:12.988 # +failover-state-select-slave master mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:13.055 # +selected-slave slave 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:13.055 * +failover-state-send-slaveof-noone slave 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:13.133 * +failover-state-wait-promotion slave 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:13.205 # +promoted-slave slave 127.0.0.1:26379 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:13.205 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:13.292 * +slave-reconf-sent slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:14.015 # -odown master mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:14.015 * +slave-reconf-inprog slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:14.015 * +slave-reconf-done slave 127.0.0.1:16379 127.0.0.1 16379 @ mymaster 127.0.0.1 6379
21051:X 01 Jul 17:33:14.071 # +failover-end master mymaster 127.0.0.1 6379

//故障转移成功
21051:X 01 Jul 17:33:14.071 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 26379

再打开sentinel_16380.conf查看配置文件,是否被修改。

// 哨兵监控配置节点已从
sentinel monitor mymaster 127.0.0.1 26379 2

// 同时被追加以下配置
# Generated by CONFIG REWRITE
sentinel known-slave mymaster 127.0.0.1 16379
sentinel known-slave mymaster 127.0.0.1 6379
sentinel known-sentinel mymaster 127.0.0.1 36380 7d011dd04392288cdf5cde9b3f5ec8fee9d8dcd5
sentinel known-sentinel mymaster 127.0.0.1 26380 cb19acb18a984bd016f47c8c61c13a5b5c7e112d
sentinel current-epoch 1

可以,看到哨兵集群确实是实现了自动故障转移。

哨兵模式未解决的问题

哨兵模式虽然实现了自动故障转移功能,但是没能解决写操作无法负载均衡,存储的能力受到单机的限制。解决的方案是使用Redis集群,具体的集群解决可以参考这篇掘金文章 深入剖析Redis系列(二) - Redis哨兵模式与高可用集群

掘金用户零壹技术栈的文章------深入剖析Redis系列(二) - Redis哨兵模式与高可用集群

Redis官方文档---High Availability