一文搞懂哨兵

众所周知,Redis 由于其利用内存来存储数据,且支持非常丰富的数据结构,包括字符串、列表、集合、甚至地理位置信息(GEO)等等,被广泛应用在各种在各个领域实现缓存功能。但是在实际的生产环境中,要希望发挥Redis 的缓存功能,保障业务的正常运行,高可用是一个绕不开的话题。今天我们就来讨论下Redis 的高可用实现方案——哨兵。

在介绍哨兵之前,我们需要先了解下 Redis 的主从复制模式,Redis 的主从复制模式是为了解决在分布式系统中出现的单点问题,通过把数据复制多个副本部署到多台机器上,从而实现满足故障恢复和负载均衡等需求。但是这种复制模式一旦主节点出现故障无法提供服务,需要人工介入手工将从节点调整为主节点,同时应用端还需要修改新的主节点地址。

如图所示,主节点无法正常启动,需要选出一个从节点 (slave-1),对其执行slaveof no one命令使其成为新的主节点,即原来的从节点(slave-1)成为新的主节点后,更新应用方的主节点信息,重新启动应用方。

这种人工介入的故障转移方式对于需要高可用的业务应用场景是不能容忍的。因此 Redis 提供了一种更为可靠的方式,即哨兵。

什么是哨兵

Redis 哨兵 (Sentinel) 是一个分布式的架构,其自身也是一个独立的 Redis 节点,只不过不存储数据且只支持部分命令,它能够自动完成故障发现和故障转移,并通知应用方,从而实现高可用。

Redis Sentinel 包含了若干 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其他 Sentinel 节点进行监控,当发现节点异常时,会对节点做下线标识,如果被标识的是主节点,此时会与其他Sentinel 节点进行协商,当大多数Sentinel 节点都人为主节点不可达时候,会发起选举,选出一个 Sentinel 节点来完成自动故障转移的工作,同时会将这个变化通知给 Redis 的应用方。这个过程是完全自动化的,无需人工干预。

哨兵主要提供一下几个功能:

Sentinel 主要提供以下几个功能:

  • 监控。Sentinel会不断检查您的主实例和副本实例是否按预期工作。
  • 通知。Sentinel可以通过API通知系统管理员或其他计算机程序,其中一个受监视的Redis实例出了问题。
  • 自动故障转移。如果主服务器未按预期工作,则Sentinel可以启动故障转移过程,在该过程中将副本升级为主服务器,将其他附加副本重新配置为使用新的主服务器,并通知使用Redis服务器的应用程序要使用的新地址。连接时。
  • 配置提供程序。Sentinel充当客户端服务发现的授权来源:客户端连接到Sentinels,以询问负责给定服务的当前Redis主服务器的地址。如果发生故障转移,Sentinels将报告新地址。

搭建哨兵

Sentinel 在最新的版本中使用的版本是 Sentinel 2。其使用更强大且更容易预测的算法对Sentinel初始实现进行的重写。

在搭建哨兵之前,需要掌握一些基本常识:

  • 一个高可用的哨兵集群需要至少3个Sentinel实例。
  • 需要将三个Sentinel实例放置到被认为以独立方式发生故障的计算机或虚拟机中。换句话说就是部署在不同的机器上。
  • Sentinel + Redis分布式系统不保证在故障期间保留已确认的写入,因为Redis使用异步复制。
  • 使用容器或其他形式的网络地址转换或端口映射时要注意,例如Docker执行端口重新映射会破坏Sentinel对其他Sentinel进程的自动发现以及主副本的列表。

为什么需要至少3个Sentinel 实现哨兵?

因为多个 Sentinel 节点来共同判断故障,可以有效防止误判,同时如果个别 Sentinel 节点不可用,整个 Sentinel 节点集合依然是高可用的。

哨兵的部署分为:

  • 部署数据节点
  • 部署Sentinel

如图所示

部署数据节点

1.创建一个 文件名为 redis-6379.conf 的文件,其内容如下

1
2
3
4
5
6
7
8
9
port 6379 

daemonize yes

logfile "6379.log"

dbfilename "dump-6379.rdb"

dir "/opt/soft/redis/data/"

然后启动

1
redis-server redis-6379.conf

并测试是否可以正常连接

1
$ redis-cli -h 127.0.0.1 -p 6379 ping PONG

2.复制上述文件,并修改文件名以及文件内容中的端口号为 6380和8381,以上述同样的方式创建并启动另外2个数据节点,如下所示,注意这2个从节点最后一行配置与主节点不一样。

redis-6380.conf

1
2
3
4
5
6
7
8
9
10
port 6380

daemonize yes

logfile "6380.log"

dbfilename "dump-6380.rdb"

dir "/opt/soft/redis/data/"
slaveof 127.0.0.1 6379

redis-6381.conf

1
2
3
4
5
6
7
8
9
10
port 6381

daemonize yes

logfile "6381.log"

dbfilename "dump-6381.rdb"

dir "/opt/soft/redis/data/"
slaveof 127.0.0.1 6379

3.当主从搭建完成后,使用如下命令确认主从关系是否正常

1
redis-cli -h 127.0.0.1 -p 6379 info replication

部署sentinel 节点

1.创建一个文件名为redis-sentinel-26379.conf 的文件,Sentinels默认情况下会监听TCP端口26379的连接。

1
2
3
4
5
6
7
8
port 26379
daemonize yes
logfile "26379.log"
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

配置文件说明:

  • Sentinel节点的默认端口是 26379。

  • sentinel monitor mymaster 127.0.0.163792 配置代表 sentinel-1节点需要监控127.0.0.1:6379 这个主节点,2代表判断主节点失败至少需要2个 Sentinel 节 点同意,mymaster 是主节点的别名。其配置为 sentinel monitor ,quorum 表示要判断主节点最终不可达所需要的票数。同时这个参数还与选举领导者有关,至少需要max(quorum,num/2+1)个节点参与选举,才能选出领导者 sentinel,从而完成故障转移。比如总共有 5 个 sentinel 节点,quorum =4 ,name 至少需要 4 个sentinel 节点才可以进行领导者的选举。

  • sentinel down-after-milliseconds 表示每个 sentinel 节点都要定期发送 ping 命令来判断 redis 数据节点和其他 sentinel 节点是否可达,如果超过了down-after-milliseconds 配置的时间且没有有效回复,则判断节点不可达。times 单位是毫秒。
    down-after-milliseconds虽然以为参数,但实际上对 Sentinel节点、主节点、从节点的失败判定同时有效。

  • sentinel parallel-syncs 当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs 就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数。如果这个参数配置的比较大,那么多个从节 点会向新的主节点同时发起复制操作,尽管复制操作通常不会阻塞主节点, 但是同时向主节点发起复制,必然会对主节点所在的机器造成一定的网络和 磁盘IO开销。

  • sentinel failover-timeout failover-timeout通常被解释成故障转移超时时间,但实际上它作用于故障转移的各个阶段:

    • a) 选出合适从节点。
    • b) 晋升选出的从节点为主节点。
    • c) 命令其余从节点复制新的主节点。
    • d) 等待原主节点恢复后命令它去复制新的主节点。

    failover-timeout的作用具体体现在四个方面:

    • 如果Redis Sentinel对一个主节点故障转移失败,那么下次再对该主节点做故障转移的起始时间是failover-timeout的2倍。
    • 在b)阶段时,如果Sentinel节点向a)阶段选出来的从节点执行slaveof no one一直失败(例如该从节点此时出现故障),当此过程超过 failover-timeout时,则故障转移失败。
    • 在b)阶段如果执行成功,Sentinel节点还会执行info命令来确认a)阶段选出来的节点确实晋升为主节点,如果此过程执行时间超过failover- timeout时,则故障转移失败。
    • 如果c)阶段执行时间超过了failover-timeout(不包含复制时间), 则故障转移失败。注意即使超过了这个时间,Sentinel节点也会最终配置从 节点去同步最新的主节点。

2.启动节点

1
redis-sentinel redis-sentinel-26379.conf
  1. 确认sentinel 是否正常,Sentinel节点本质上是一个特殊的Redis节点,所以也可以通过info命令来查询它的相关信息,从下面info的Sentinel片段来看
1
redis-cli -h 127.0.0.1 -p 26379 info Sentinel

4.然后使用使用上面的配置文件,创建redis-sentinel-26380.conf 和redis-sentinel-26381.conf ,其配置文件内容如下:

redis-sentinel-26380.conf

1
2
3
4
5
6
7
8
port 26380
daemonize yes
logfile "26380.log"
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

redis-sentinel-26381.conf

1
2
3
4
5
6
7
8
port 26381
daemonize yes
logfile "26381.log"
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

当三个sentinel 节点启动后,配置文件中的内容发生了变化:

  • Sentinel节点自动发现了从节点、其余Sentinel节点。
  • 去掉了默认配置,例如parallel-syncs、failover-timeout参数。
  • 添加了配置纪元相关参数。

Sentinel 的三个定时监控任务:

  • 每隔 10 秒向主节点和从节点发送 info 命令获取最新的拓扑。
  • 每隔 2 秒,每个 sentinel 节点会向数据节点的_sentinel_:hello频道发送该 sentinel 节点对于主节点的判断以及当前 sentinel 节点信息,同时每个 sentinel 节点也会订阅该频道,来了解其他 sentinel 节点以及他们对主节点的判断。
  • 每个 1 秒,每个 sentinel 节点会向主节点、从节点、其他 sentinel 节点发送一条 ping 命令做一次心跳检测,判断节点是否存活。

哨兵的下线

哨兵的主观和客观下线

  • 主观下线:当节点超过 down-after-milliseconds 没有进行有效回复,就会判定该节点失败,这叫主观下线。
  • 客观下线: 当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel is- master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过 个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会 做出客观下线的决定。

哨兵的选举

哨兵的选举过程如下:

  • 每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令, 要求将自己设置为领导者。
  • 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。
  • 如果该Sentinel节点发现自己的票数已经大于等于max(quorum, num(sentinels)/2+1),那么它将成为领导者。
  • 如果此过程没有选举出领导者,将进入下一次选举。

领导者选举的过程非常快,基本上谁先完成客观下线,谁就是领导者。

如何进行故障转移

故障转移,在从节点列表中选出一个节点作为新的主节点,选择方法如下:

  • 过滤:“不健康”(主观下线、断线)、5秒内没有回复过Sentinel节 点ping响应、与主节点失联超过down-after-milliseconds*10秒。
  • 选择slave-priority(从节点优先级)最高的从节点列表,如果存在返回,不存在则继续。
  • 选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续。
  • 选择runid最小的从节点。

客户端连接

以Java 为例,可以通过 sentinels.add 添加多个sentinel 节点,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RedisSentinelClient {
public static void main(String[] args) {
Set sentinels = new HashSet();
sentinels.add(new HostAndPort("10.12.37.71", 26379).toString());
sentinels.add(new HostAndPort("10.12.37.72", 26380).toString());
sentinels.add(new HostAndPort("10.12.37.73", 26381).toString());
JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels);
System.out.println("Current master: " + sentinelPool.getCurrentHostMaster().toString());

Jedis master = sentinelPool.getResource();
master.set("username",“csdn");
sentinelPool.returnResource(master);

Jedis master2 = sentinelPool.getResource();
String value = master2.get("username");
System.out.println("username: " + value);
master2.close();
sentinelPool.destroy();
}
}

好了,以上就是关于redis 哨兵的相关介绍。