哨兵模式
-
- 1、哨兵架构概述
- 2、哨兵架构的搭建
-
- 2.1、运行Sentinel
- 2.2、Sentinel配置
-
- [2.2.1、sentinel monitor](#2.2.1、sentinel monitor)
- 2.2.2、其他的Sentinel选项
- 2.2.3、Sentinel部署示例
- 2.2.4、Sentinel、Docker、NAT和可能的问题
- [2.3、Windows 10搭建Redis 5版本哨兵架构](#2.3、Windows 10搭建Redis 5版本哨兵架构)
- 2.4、Linux系统哨兵架构的搭建和验证
- [3、Sentinel API](#3、Sentinel API)
- 4、哨兵细节原理分析
- 5、客户端访问哨兵架构的系统
1、哨兵架构概述
Redis Sentinel(哨兵或哨岗)为Redis提供了高可用解决方案。Sentinel实例可以监视任意多个主节点以及这些主节点属下的所有从节点,并在被监视的主节点进入下线状态时自动将下线主节点属下的从节点升级为新的主节点,然后由新的主节点替代已经下线的主节点并继续处理后续的命令请求。
实际上,这意味着使用Sentinel可以部署一套Redis,在没有人为干预的情况下去应付各种各样的失败事件。Redis Sentinel同时提供了一些其他的功能,比如监控、通知、为客户端提供配置等。哨兵架构图如图所示。

哨兵架构由两部分组成,哨兵节点和数据节点:
哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的Redis节点,不存储数据。数据节点:主节点和从节点都是数据节点。
下面是Sentinel的功能列表:
监控(Monitoring):哨兵节点不断地检查用户的主从实例是否按照预期在工作。通知(Notification):如果被监控的Redis实例有问题,哨兵节点可以通过一个API来通知系统管理员或者其他应用程序。自动故障转移(Automatic Failover):如果一个主节点没有按照预期工作,哨兵节点会启动故障转移过程,把一个从节点提升为主节点,重新配置其他的从节点并使用新的主节点,使用Redis服务的应用程序在连接的时候也会被通知使用新的主节点地址。配置提供者(Configuration Provider):哨兵节点为客户端提供服务来源,对于指定的服务,客户端连接到Sentinel来寻找当前主节点的地址。当发生故障转移时,哨兵节点将报告新的主节点地址。
2、哨兵架构的搭建
哨兵节点也是一台Redis服务器,只是不对外提供任何服务,Redis bin目录下的redis-sentinel其实就是redis-server的软连接。
Sentinel 2使用更强更简单的预测算法重写了Sentinel初始化的实现部分。
Redis Sentinel的一个稳定版本是随Redis 2.8和3.0(Redis的稳定版)发布的。它的最新进展在unstable分支下进行,一旦新的特性是稳定的,就会被合并到2.8和3.0版中。与Redis 2.6一起的Redis Sentinel 1过时了,在下文主要来演示Sentinel 2的搭建过程。
2.1、运行Sentinel
如果想运行redis-sentinel,可以执行下面的命令:
bash
redis-sentinel /path/to/sentinel.conf
另外,用户也可以直接运行redis-server并以Sentinel模式来启动:
bash
redis-server /path/to/sentinel.conf --sentinel
以上这两种运行方式是一样的。
不管怎么样,必须使用一个配置文件来运行Sentinel,这个配置文件被系统用于存储当前状态。如果重启Sentinel,那么这些状态会被重新载入。如果没有配置文件或者配置文件的路径不对,Sentinel就会拒绝启动。
默认情况下,Sentinel监听TCP端口26379,所以为了让Sentinel运行,端口26379就必须是打开的,用来接收其他Sentinel实例的连接,否则Sentinel就不能互相交流,也不知道应该干什么,也不会执行故障转移。
部署之前需要了解关于Sentinel的基本内容:
- 哨兵节点最少三台并且必须为单数。这个与其他分布式框架(如Zookeeper)类似,如果是双数,在"选举"的时候就会出现平票的情况,所以必须是三台或三台以上的单数。
- 哨兵节点最少三台并且必须为单数。这个与其他分布式框架(如Zookeeper)类似,如果是双数,在"选举"的时候就会出现平票的情况,所以必须是三台或三台以上的单数。
- 三个Sentinel实例应该放在独立的物理机电脑或虚拟机中,也就是在不同的物理机中或者在不同的可用区域上执行的虚拟机中。
- Sentinel+Redis分布式系统在失败期间并不能确保写入请求被保存,因为Redis使用异步复制。有很多部署Sentinel的方式把写入限制在特定的时间窗口内,当然也有其他不安全的部署方式。
- 如果因为在开发环境或者生产环境中通过了测试而没有设置安全的高可用,那么就可能会因为一个错误的配置而导致主机宕机,以至不能继续提供正常的服务。
2.2、Sentinel配置
Redis源码中包含一个名为sentinel.conf的文件,它是一个可以用来配置Sentinel的示例配置文件。一个典型的最小配置文件如下:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
我们只需要指定要监控的主节点,并给每个单独的主节点一个不同的名称,不需要指定从节点,从节点会被自动发现,Sentinel将会根据从节点额外的信息自动更新配置(为了在重启时保留信息)。在故障转移中,每当一个从节点被提升为主节点或者一个新的Sentinel节点被发现时,配置信息会被重新写入。
如上示例的配置中监控了两个Redis实例集合(一个叫mymaster,一个叫resque),每个集合由一个主节点和不明确数量的从节点组成。
2.2.1、sentinel monitor
sentinel monitor参数的意思如下:
bash
sentinel monitor <master-group-name> <ip> <port> <quorum>
为了更加清晰明了,让我们来检查上面示例中第一行配置选项的意思:Redis监控一个名为mymaster的主节点,地址是127.0.0.1,端口号是6379,并且有两个仲裁机器。除了quorum参数,其他参数的意思都很明确:
- quorum是指同意主节点不可用所需的Sentinel的数量。
- quorum在检测到失败后投票时使用。为了执行故障转移,Sentinel实例中的一个需要被选定为领导者(leader)并且被授权进行操作,由大多数Sentinel实例进行投票。
如果有5个Sentinel实例(或称为进程),其中一个主节点quorum被设置为2,就会出现如下情况:
- 同时有两个Sentinel实例同意主节点不可用,其中的一个将会尝试开始故障转移。
- 如果至少有三个Sentinel实例同意主节点是可用的,那么故障转移就会被授权并开始执行。
在实际工作中,这意味着在失败时如果大多数的Sentinel实例没有同意,那么Sentinel永远不会开始故障转移。
2.2.2、其他的Sentinel选项
其他的选项几乎都是如下形式:
bash
sentinel <option_name> <master_name> <option_value>
其作用如下:
down-after-milliseconds:当一个Redis实例失去联系(要么不回复我们的请求,要么回复一个错误)超过了这个指定的时间(以毫秒为单位),就可以认为这个Redis实例挂掉了。parallel-syncs:设置从节点的数量,这些从节点在一次故障转移过后可以使用新的主节点进行重新配置。数量越少,完成故障转移过程花费的时间越多。如果从节点为旧的数据提供服务,那么我们或许不想让所有的从节点都使用主节点进行重新同步。复制进程对于从节点来说大部分是非阻塞式的,不过还有一个时刻它会停下来去从主节点加载数据。可以通过将这个值设为1来保证每次只有一个从节点处于不能处理命令请求的状态。
2.2.3、Sentinel部署示例
示例1:只有两个Sentinel(架构图如图所示)
在这个设置中,box1和box2是单独的物理主机,如果Master-1宕掉了,Slave-1将会被提升为主节点,因为两个Sentinel实例将会达成一致(把quorum设置为1),并且授权开始一个故障转移,因为此时大多数是两个(majority=2)。在表面上这样做上去是可行的,但是这种配置是有问题的。

如果Master-1的box1停止工作,那么Sentinel-1也会停止,运行在box2中的Sentinel-2将不会被授权进行故障转移,所以系统将不可用,结果如图所示,导致两个box之间断开连接。

需要注意的是,为了应付不同的故障,最新的配置稍后会传播给所有的Sentinel实例。在上述设置中,当单独一边的故障转移能力出现问题时,将是非常危险的。
在上面的配置中,我们完美对称地创建了两个主节点(假设Sentinel-2在没有授权的情况下可以进行故障转移),出现了两个节点之间的网络通信问题,客户端或许不确定应该写往哪一边,并且没有办法理解当分区治愈时哪一边的配置是正确的,所以至少需要部署三个Sentinel实例在三个不同的box中。
示例2:三个box的基本配置
这是一个非常简单的配置,优点是更加安全。它基于三个box,每个box运行一个Redis实例和Sentinel实例。基于三个box搭建的哨兵架构图如图所示。

如果Master1挂掉,Sentinel-2和Sentinel-3将认同这次失败,并且能授权开始一次故障转移,这样使客户端可以继续使用。
在每一个Sentinel配置中,Redis都是异步复制的,总是会有丢失一些写入数据的危险,例如当一个从节点被提升为主节点时一个写入确认尚未到达。然而,在上面的设置中,还有一种更加危险的情况:客户端和一个旧的主节点在一个网络分区中,如图所示,box1、box2和box3失去了连接。

在这种情况下,网络分区把旧的主节点(Master-1)给孤立了,所以从节点Master-2被提升为主节点。然而,当客户端和旧的主节点在同一个网络分区中,可能还在继续向旧的主节点写入数据。当网络治愈之后,这些数据将永久丢失,因为这个旧的主节点将会被重新配置,作为新的主节点下的一个从节点,并要丢弃它自己的数据,从而造成数据丢失。
可以使用下面的Redis复制特性解决这个问题,如果一个主节点发现它不再能够把写入请求发送给指定数量的从节点,它就会停止接收写入请求。
min-slaves-to-write 1
min-slaves-max-lag 10
当上面的配置应用于一个Redis实例时,Redis发现它至少不能写入一个从节点时,作为主节点的Redis将会停止接收写入请求。复制是异步的,不能写入就意味着从节点也是断开的,或者超过了指定的max-lag秒数没有发送异步回应。
在上面的示例中,使用这个配置的旧的主节点Master-1,在10秒过后就不可用了。当分区治愈时,Sentinel配置将会统一为最新的配置,客户端将获取一个有效的配置并且继续发送命令请求。
经过这种改进,如果两个从节点挂掉了,主节点将会停止接收写入请求,这就是一个权衡的处理方式。
示例3: Sentinel在客户端所在的box中
有时我们只有两个Redis box是可用的:一个给主节点,一个给从节点。在这种情况下,示例2中的配置是不可行的,我们可以将Sentinel放置在客户端所在的box中,如图所示。

在这种配置下,Sentinel的视角和客户端是一样的:如果大部分的客户端认为一个主节点是可用的,它就是可用的。这里的Client-1、Client-2、Client-3是普通的客户端,并不意味着Client-1是连接到Redis的单个客户端,而更像是一个应用服务器或者一个Redis App。
如果Master-1和Slave-1所在的box-1挂掉了,将会进行故障转移,但是不同的网络分区将导致不同的行为。比如,客户端和Redis服务断开连接(box3、box4、box5在同一个网络分区,连接不到box1、box2)时,Sentinel将不会被设置,因为Redis的主节点和从节点都是不可用的。
如果Client-3和Master-1(box5和box1在同一个局域网)在同一个网络分区,我们有了一个和示例2中描述的类似问题,不同的是这里无法打破对称,因为只有一个主节点和从节点,所以主节点不会停止接收命令请求,因为5个哨兵实例只要一个认为主节点存在问题,但是由于quorum设置为2,也就是说最少需要两个哨兵实例认为主节点存在问题,才会让主节点停止接收命令请求。
这虽然是一个有效的设置,但是示例2中的设置更有优势,比如Redis高可用系统,Redis运行在同一个box中,更容易被管理,并且可以限制在小部分的网络分区中主节点接收写入请求的时间。
示例4: Sentinel客户端这一边少于三个客户端
在示例3描述的配置中,如果客户端这一边的box少于三个,这种配置就不能使用。在这种情况下,我们需要借助混合配置,如图所示。

这与示例3中的配置非常相似,但是这里我们在可用的4个box中运行了4个Sentinel实例。如果主节点Master-1变成不可用的节点,那么其他三个Sentinel实例将执行故障转移。
从理论上讲,当移除Sentinel-2和Sentinel-4正在运行的box时把quorum设置为2,这个设置可以正常工作。然而,在应用层没有高可用的系统时,想在Redis这一边获得高可用性是不太可能的。
2.2.4、Sentinel、Docker、NAT和可能的问题
运行在Docker容器中的程序可能被暴露在宿主机不同的端口上。在相同的服务器上同时使用同一个端口部署和运行多个容器是Docker具有的端口映射技术。
Docker不是唯一使用端口映射技术的软件系统,其他的网络地址转换设置也会导致端口被重映射(有时候只是IP地址进行重映射)。
端口和地址重映射在两个方面会引发与Sentinel有关的问题:
- Sentinel的自动发现服务将停止工作,因为这种服务是通过每个Sentinel实例往它所监听的端口和IP地址广播"hello"消息来实现的。但是Sentinel实例没有办法理解端口和IP地址被重映射了这种情况,所以会宣布它和其他Sentinel实例的连接是不正常的。
- 在一个主从节点的INFO输出中可以发现类似的问题:主节点检查远端对等的TCP连接来发现地址,在握手过程中,从节点广播自己的端口,然而端口是错误的。
Sentinel自动发现从节点在使用主节点的INFO输出信息,但是所发现的从节点是不可达的,并且Sentinel将永远不会开始故障转移,因为从系统的观点来看没有可用的从节点,所以目前不存在可用的Docker部署的主节点和从节点实例,除非我们通知Docker以1:1的方式来映射端口。
对于第一个问题,如果想使用Docker运行一堆Sentinel实例,就可以使用下面的两个Sentinel配置宣布一个指定的IP地址和端口:
sentinel announce-ip <ip>
sentinel announce-port <port>
上述两个配置命令在工作环境中非常有用,因为NAT可以通过非本地地址从外部访问Sentinel。当提供了announce-ip时,Sentinel将在通信中声明指定的IP地址,而不是像通常那样自动检测本地地址。类似地,当提供了announce-port有效且非零时,Sentinel将宣布指定的TCP端口。这两个选项不需要一起使用,如果只提供announce-ip,那么Sentinel将宣告指定的IP地址和port选项指定的服务器端口。如果仅提供announce-port,那么Sentinel将宣告自动检测到的本地IP地址和指定的端口。
注意,Docker以host networking模式运行就不会有问题,因为端口不会被重新映射。
2.3、Windows 10搭建Redis 5版本哨兵架构
2.3.1、快速搭建
为了快速让初学者搭建哨兵(Sentinel)架构,下面在Windows 10系统上搭建Redis 5版本的哨兵架构(即以哨兵模式运行)。
1)创建配置文件Sentinel\redis-7010.conf(主节点启动的配置文件)。
port 7010
repl-diskless-sync yes
repl-diskless-sync-delay 0
maxmemory 100mb
appendonly no
dir "../Temp"
dbfilename "sentinel-target-7010.rdb"
save ""
2)创建配置文件Sentinel\redis-7011.conf(从节点启动的配置文件)。
port 7011
slaveof 127.0.0.1 7010
repl-diskless-sync yes
repl-diskless-sync-delay 0
maxmemory 100mb
appendonly no
dir "../Temp"
dbfilename "sentinel-target-7011.rdb"
save ""
3)创建配置文件Sentinel\sentinel-26379.conf(哨兵节点启动的配置文件)。
port 26379
sentinel monitor mymaster 127.0.0.1 7010 1
sentinel down-after-milliseconds mymaster 1000
sentinel failover-timeout mymaster 1000
sentinel config-epoch mymaster 0
dir "../Temp"
4)创建配置文件Sentinel\sentinel-26380.conf(哨兵节点启动的配置文件)。
port 26380
sentinel monitor mymaster 127.0.0.1 7010 1
sentinel down-after-milliseconds mymaster 1000
sentinel failover-timeout mymaster 1000
sentinel config-epoch mymaster 0
dir "../Temp"
5)创建配置文件Sentinel\sentinel-26381.conf(哨兵节点启动的配置文件)。
port 26381
sentinel monitor mymaster 127.0.0.1 7010 1
sentinel down-after-milliseconds mymaster 1000
sentinel failover-timeout mymaster 1000
sentinel config-epoch mymaster 0
dir "../Temp"
6)创建批处理文件start-sentinel.cmd。
@echo off
echo Starting Sentinel:
pushd %~dp0\Sentinel
echo Targets: 7010-7011
@start "Redis (Sentinel-Target): 7010" /min ..\5.0.1\redis-server.exe
redis-7010.conf
@start "Redis (Sentinel-Target): 7011" /min ..\5.0.1\redis-server.exe
redis-7011.conf
echo Monitors: 26379-26381
@start "Redis (Sentinel): 26379" /min ..\5.0.1\redis-server.exe
sentinel-26379.conf --sentinel
@start "Redis (Sentinel): 26380" /min ..\5.0.1\redis-server.exe
sentinel-26380.conf --sentinel
@start "Redis (Sentinel): 26381" /min ..\5.0.1\redis-server.exe
sentinel-26381.conf --sentinel
popd
文件目录如图所示。此时双击start-sentinel.cmd即可启动此哨兵架构。

Sentinel实例架构图如图所示。

如下是主节点的启动日志:
bash
[54228] 01 Feb 23:44:50.365 # Server initialized
[54228] 01 Feb 23:44:50.365 * Ready to accept connections
[54228] 01 Feb 23:44:50.436 * Replica 127.0.0.1:7011 asks for synchronization
[54228] 01 Feb 23:44:50.437 * Full resync requested by replica 127.0.0.1:7011
[54228] 01 Feb 23:44:51.370 * Starting BGSAVE for SYNC with target: replicas
sockets
[54228] 01 Feb 23:44:51.447 * Background RDB transfer started by pid 29432
[54228] 01 Feb 23:44:51.548 # fork operation complete
[54228] 01 Feb 23:44:51.556 * Background RDB transfer terminated with success
[54228] 01 Feb 23:44:51.557 # Slave 127.0.0.1:7011 correctly received the
streamed RDB file.
[54228] 01 Feb 23:44:51.559 * Streamed RDB transfer with replica 127.0.0.1:7011
succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming
[54228] 01 Feb 23:44:52.446 * Synchronization with replica 127.0.0.1:7011
succeeded
如下是从节点的启动日志:
bash
[23244] 01 Feb 23:44:50.430 # Server initialized
[23244] 01 Feb 23:44:50.431 * Ready to accept connections
[23244] 01 Feb 23:44:50.431 * Connecting to MASTER 127.0.0.1:7010
[23244] 01 Feb 23:44:50.432 * MASTER <-> REPLICA sync started
[23244] 01 Feb 23:44:50.432 * Non blocking connect for SYNC fired the event.
[23244] 01 Feb 23:44:50.432 * Master replied to PING, replication can continue...
[23244] 01 Feb 23:44:50.436 * Partial resynchronization not possible (no cached
master)
[23244] 01 Feb 23:44:51.370 * Full resync from master:
07d6f7ef5428663ca799631538a820a810334f8a:0
[23244] 01 Feb 23:44:51.506 * MASTER <-> REPLICA sync: receiving streamed RDB
from master
[23244] 01 Feb 23:44:51.538 * MASTER <-> REPLICA sync: Flushing old data
[23244] 01 Feb 23:44:51.539 * MASTER <-> REPLICA sync: Loading DB in memory
[23244] 01 Feb 23:44:51.540 * MASTER <-> REPLICA sync: Finished with success
通过日志可以看出,主从架构没有任何问题。下面是三个哨兵节点输出的相同日志:
bash
[53232] 01 Feb 23:44:50.604 # Sentinel ID is
eafb9ff17f76aabe1dd3c1c55d33d2c33a77977e
[53232] 01 Feb 23:44:50.604 # +monitor master mymaster 127.0.0.1 7010 quorum 1
[53232] 01 Feb 23:44:50.605 * +slave slave 127.0.0.1:7011 127.0.0.1 7011 @
mymaster 127.0.0.1 7010
[53232] 01 Feb 23:44:52.543 * +sentinel sentinel
2db434d2dbcd6cf3a00ca745a3b6ba50a5e0ce54 127.0.0.1 26379 @ mymaster 127.0.0.1 7010
[53232] 01 Feb 23:44:52.624 * +sentinel sentinel
41afccaf5c2ac958d4748eb50577ffa4166a06e6 127.0.0.1 26381 @ mymaster 127.0.0.1 7010
通过如上信息可以看出,哨兵节点不仅保存了主节点信息还保存了从节点的信息,可以在后期主节点出现问题的情况下把从节点提升为主节点。
登录端口号为7010的节点去查询主从节点的信息,输出如图所示。从中可以看出目前7011是从节点,而且从节点是不可以进行写入操作的。

2.3.2、故障转移
手动关闭端口号为7010的主节点窗体,然后可以看到哨兵节点的日志输出:
bash
[53232] 02 Feb 00:12:12.891 # +vote-for-leader
41afccaf5c2ac958d4748eb50577ffa4166a06e6 1
[53232] 02 Feb 00:12:12.896 # +sdown master mymaster 127.0.0.1 7010
[53232] 02 Feb 00:12:12.896 # +odown master mymaster 127.0.0.1 7010 #quorum
1/1
[53232] 02 Feb 00:12:12.896 # Next failover delay: I will not start a failover
before Tue Feb 2 00:12:15 2021
[53232] 02 Feb 00:12:13.976 # +config-update-from sentinel
41afccaf5c2ac958d4748eb50577ffa4166a06e6 127.0.0.1 26381 @ mymaster 127.0.0.1 7010
[53232] 02 Feb 00:12:13.977 # +switch-master mymaster 127.0.0.1 7010 127.0.0.1
7011 # 从节点切换为主节点
[53232] 02 Feb 00:12:13.977 * +slave slave 127.0.0.1:7010 127.0.0.1 7010 @
mymaster 127.0.0.1 7011
[53232] 02 Feb 00:12:14.998 # +sdown slave 127.0.0.1:7010 127.0.0.1 7010 @
mymaster 127.0.0.1 7011
从上面的输出信息可以看出,端口号为7011的从节点被提升为主节点。再次登录7011端口,执行info命令查询节点信息。
bash
127.0.0.1:7011> info Replication
# Replication
role:master
connected_slaves:0
master_replid:d25d2dd2a0195a459017a8088b79c1a28b89087e
master_replid2:07d6f7ef5428663ca799631538a820a810334f8a
master_repl_offset:412666
second_repl_offset:323534
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:412666
127.0.0.1:7011>
该节点角色已经变成主节点,因为目前只剩下一个节点,connected_slaves:0表示已经没有从节点了。
综合上面的信息可以得出:当主节点宕机之后,哨兵模式自动实现了故障转移、实现了高可用。
2.4、Linux系统哨兵架构的搭建和验证
接下来我们将在Linux系统中演示搭建哨兵架构的过程。
1)启动三个Redis服务,分别是192.168.3.201:6379、192.168.3.202:6379、192.168.3.203:6379。
2)启动三个哨兵服务,分别是192.168.3.201:26381、192.168.3.202:26381、192.168.3.203:26381。
3)192.168.3.201上的服务为Redis初始主节点,192.168.3.202和192.168.3.203上的服务分别为从节点。
4)192.168.3.201:26381、192.168.3.202:26381、192.168.3.203:26381作为三个哨兵服务来监控上面的Redis主从架构,如图所示。

2.4.1、相关配置
在安装哨兵之前,可以参照之前在Linux上安装Redis的说明,首先把Redis编译和安装到/home/redis/redis-6.0.9/src/目录。以192.168.3.201为例在/home/redis/redis-6.0.9/src/目录中修改redis.conf、新建sentinel.conf文件。redis.conf配置文件如下:
port 6379 # 三台服务一致
daemonize yes # 三台服务一致
pidfile "/var/run/redis6379.pid" # 三台服务一致
logfile "/home/redis/redis-6.0.9/src/redis_6379.log" # 需要手动touch文件
dir "/home/redis/redis-6.0.9/src/data" # 需要手动mkdir文件夹
192.168.3.202和192.168.3.203的配置文件如下:
port 6379 # 三台服务一致
slaveof 192.168.3.201 6379 # 提供192.168.3.201: 6379服务的从节点
daemonize yes # 三台服务一致
pidfile "/var/run/redis6379.pid" # 三台服务一致
logfile "/home/redis/redis-6.0.9/src/redis_6379.log" # 需要手动touch文件
dir "/home/redis/redis-6.0.9/src/data" # 需要手动mkdir文件夹
sentinel.conf配置文件如下(三台机器的配置文件一致):
port 26381 # 三台服务一致
daemonize yes # 三台服务一致
pidfile /var/run/redis-sentinel_26381.pid # 三台服务一致
logfile "/home/redis/redis-6.0.9/src/sentinel_26381.log" #需要手动touch文件
dir /tmp/sentinel_26381 #需要手动mkdir文件夹
sentinel monitor mymaster 192.168.3.201 6379 2 #都监听6379端口
启动Redis服务:
cd /home/redis/redis-6.0.9/src # 进入redis 安装目录
redis-server redis.conf
启动Sentinel服务:
cd /home/redis/redis-6.0.9/src # 进入redis 安装目录
redis-server sentinel.conf --sentinel
2.4.2、询问Sentinel关于主节点的状态
检查主节点的监控是否正常:
bash
redis-cli -h 192.168.3.201 -p 26381
192.168.3.201:26381> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "192.168.3.201"
5) "port"
6) "6379"
如上信息说明监控主节点没有问题。
2.4.3、故障转移测试
至此,上面部署的Sentinel可以进行测试了。可以先杀死主节点,然后查看配置的变化,进入192.168.3.201,再进入目录/home/redis/redis-6.0.9/src执行如下命令:
redis-cli -p 6379 DEBUG sleep 30
这个命令让主节点变为不可达的状态,睡眠30秒,它基本上就是模拟主节点挂掉了。
检查Sentinel的日志,应该能够看到许多操作:
- 每个Sentinel发现主节点挂掉了并有一个+sdown事件。
- +sdown事件稍候升级到+odown,意味着大多数Sentinel同意了主节点是不可达的,即不可用了。
- 各个Sentinel投票选举出一个Sentinel节点并让它尝试故障转移。
- 开始故障转移。
如果重新询问mymaster当前主节点的地址,那么我们会得到一个不同的回复:
bash
redis-cli -h 192.168.3.201 -p 26381
192.168.3.201:26381> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "192.168.3.202"
5) "port"
6) "6379"
3、Sentinel API
Sentinel API可以用来检查Sentinel的状态、检查主节点和从节点的健康情况、订阅具体的通知并在运行时改变Sentinel的配置。
默认情况下,Sentinel使用TCP端口号26379。Sentinel可接收和使用Redis的协议命令,所以可以使用redis-cli或者其他未修改的Redis客户端与Sentinel交流。
可以通过使用发布与订阅机制来直接查询一个Sentinel,以检查它所监控的Redis实例状态,看看另外的Sentinel所知道的信息。每当事件发生时,比如说一次故障转移或一个实例发生错误等,就可能接收到一个从Sentinel推送过来的通知信息。
3.1、Sentinel命令
下面是可以接收的命令列表:
PING:仅仅返回PONG。SENTINEL masters:展示监控的主节点和它们的状态列表。SENTINEL master<master name>:展示指定的主节点的信息。SENTINEL salves<master name>:展示指定主节点的从节点以及它们的状态。SENTINEL sentinels<master name>:展示指定主节点的sentinel实例以及它们的状态。SENTINEL get-master-addr-by-name<mastername>:返回主节点的IP地址和端口号。如果这个主节点正在进行故障转移,就返回提升的从节点的IP地址和端 口号。SENTINEL reset<pattern>:该命令将会根据匹配的名称重置主节点,pattern参数是通配符(glob-style)类型,重置Sentinel进程清除主节点中之前的所有状态,并且移除主节点发现和关联的从节点与Sentinel。SENTINEL failover<master name>:如果主节点是不可达的,就强制开始故障转移,不需要其他的Sentinel同意。SENTINEL ckquorum<master name>:检查当前的Sentinel配置对于主节点的故障转移是否能达到仲裁人数(即同意票数),以便授权进行故障转移。这个命令应该在监控系统中使用以检查Sentinel的部署是否正常。SENTINEL flushconfig:强制Sentinel把它的配置和状态重新写入磁盘(即刷盘)。每次Sentinel状态有改变,Sentinel就会重写配置信息。有时候由于错误的操作、磁盘故障、程序包升级脚本或配置管理等原因会导致配置文件的丢失。在这种情况下,最容易的解决办法就是要强制Sentinel重写配置文件,即使之前的配置文件完全丢失了,这个命令也能很好地完成工作。
3.2、其他相关配置和说明
3.2.1、运行时重新配置Sentinel
从Redis 2.8.4开始,Sentinel提供了一个API,以增加、删除或者改变指定主节点的配置。注意,如果部署中有多个Sentinel,在某些情况下为了使它们工作正常,就需要修改所有Redis Sentinel实例的配置,这是因为修改单个Sentinel的配置并不能把修改带来的变化发送给网络中其他的Sentinel。
下面的一些SENTINEL命令可用于更新Sentinel实例的配置:
SENTINEL MONITOR<name><ip><port><quorum>:告诉Sentinel开始监控一个指定名称、IP地址、端口号、quorum的主节点。它和sentinel.conf配置文件中的sentinel monitor配置命令是完全相同的,不同的是这里不能使用主机名作为IP地址,需要提供一个IPv4或IPv6地址。SENTINEL REMOVE<name>:用来删除指定的主节点。主节点不再被监控,并且将从Sentinel的内部状态中完全删除,之后就不会被SENTINEL masters命令列出。SENTINEL SET<name><option><value>: SET命令和Redis的CONFIG SET命令非常相似,用来改变指定主节点的配置参数。可以指定多个选项值。所有通过sentinel.conf文件配置的参数都可以使用SET命令重新配置。
下面是SENTINEL SET命令的一个例子,修改一个名为objects-cache的主节点的down-after-milliseconds配置:
bash
SENTINEL SET objects-cache-master down-after-milliseconds 1000
正如我们提到的,SENTINEL SET命令可以用来设置所有在启动配置文件中设置的参数,而且可以只改变主节点的quorum配置,而不需要使用SENTINEL REMOVE和SENTINEL MONITOR命令来删除或者增加主节点,命令示例如下:
bash
SENTINEL SET objects-cache-master quorum 5
3.2.2、添加和删除Sentinel
把一个新的Sentinel添加到部署中很简单,因为Sentinel有自动发现机制。所有我们需要做的事情就是开启一个新的Sentinel来监控当前的主节点。10秒后,Sentinel将获取到含有其他Sentinel的列表和当前主节点的从节点。
如果想一次性增加多个Sentinel,建议一个接一个地增加,等当前部署中所有的Sentinel已经知道新添加的第一个Sentinel之后再添加另一个新的。在添加新的Sentinel过程中有可能会发生错误,这时要保证在网络分区内大部分Sentinel是可用的。没有网络分区时,在30秒后再增加新的Sentinel。
可以使用SENTINEL MASTER mastername命令来检查是否全部Sentinel都同意了监控主节点的Sentinel总数。
删除一个Sentinel稍微复杂一点:Sentinel永远不会忘记已经看到的Sentinel(甚至对于在相当长的一段时间内不可达的Sentinel也是如此),因为我们不想动态地改变部署中进行故障转移授权和创建新的配置所需要的大多数Sentinel。在没有网络分区时,需要执行下面的步骤来删除Sentinel:
- 停止将删除的Sentinel上的进程。
- 发送SENTINEL RESET*命令到其他的Sentinel实例,前后两次发送的时间间隔至少要30秒。
- 检查每个SENTINEL MASTER mastername命令的输出,查看所有Sentinel赞同的当前存活的Sentinel数量。
3.2.3、删除旧的主节点或不可达的从节点
Sentinel永远不会忘记一个主节点的从节点,甚至它们在很长时间内都不可达也是如此。这是很有用的,因为在发生网络分区或失败事件后,Sentinel应该能正确地重新配置一个返回的从节点。在故障转移发生之后,进行故障转移的主节点实际上被添加为新的主节点的从节点,一旦可用,该节点就将重新配置并从复制新的主节点上的数据。若想从Sentinel监控的从节点列表中永久地删除一个从节点,则可以发送SENTINEL RESETmastername命令给所有的Sentinel:这些Sentinel将在10秒后刷新从节点列表,只添加当前主节点的INFO输出中正确的节点列表。
3.2.4、哨兵日志解析
为了更好地理解故障转移流程,下面列出一些日志关键字及其说明。
+reset-master<instance details>:主节点被重置。+slave<instance details>:一个新的从节点被发现和关联。+failover-state-reconf-slaves<instance details>:故障转移状态被转换为reconf-slaves状态。+failover-detected<instance details>:另一个Sentinel开始了故障转移或者其他的外部实体被发现(一个关联的从节点变为主节点)。+slave-reconf-sent<instance details>:为了给新的从节点重新配置,Sentinel中的领导者(leader)发送SLAVEOF命令到这个实例。+slave-reconf-inprog<instance details>:从节点被重新配置,并作为主节点的从节点,但是同步过程尚未完成。+slave-reconf-done<instance details>:从节点现在和主节点是同步的。-dup-sentinel<instance details>:指定的主节点,一个或者多个Sentinel被删除(因为是重复的)。+sentinel<instance details>:主节点的一个新的Sentinel被发现和关联。+sdown<instance details>:指定的实例处于主观下线状态。-sdown<instance details>:指定的实例不再处于主观下线状态。+odown<instance details>:指定的实例处于客观下线状态。-odown<instance details>:指定的实例不处于客观下线状态。+new-epoch<instance details>:当前时间被更新。+try-failover<instance details>:准备新的故障转移,等待大多数Sentinel的投票选举。+elected-leader<instance details>:赢得了选举,开始故障转移。+failover-state-select-slave<instance details>:新的故障转移状态是select-slave,正在寻找合适提升为主节点的从节点。no-good-slave<instance details>:没有用于提升的合适从节点,一般会在稍后重试,但是这也许会改变并且终止故障转移。selected-slave<instance details>:找到了指定的从节点来进行提升。failover-state-send-slaveof-noone<instance details>:尝试重新配置提升后的主节点,等待它切换。failover-end-for-timeout<instance details>:故障转移由于超时而停止,无论如何所有从节点最后都会被配置为复制新的主节点。failover-end<instance details>:故障转移由于成功而停止,所有的从节点被配置为复制新的主节点。switch-master<master name><oldip><oldport><newip><newport>:配置改变后,主节点新的IP地址和地址都是指定的。+tilt:进入Tilt模式。-tilt:退出Tilt模式。
3.2.5、Sentinel与Redis权限
当主节点被配置为需要密码时,作为一个安全措施,从节点也需要知道这个密码以取得主节点的认证并且创建主-从连接用于异步复制协议。可以使用下列的配置选项来实现:
- requirepass在主节点中,为了设置认证密码,并且确保实例不会处理来自未认证的客户端的请求。
- masterauth在从节点中,为了取得主节点的认证,以便从主节点正确地复制数据。
当Sentinel使用时,不会是一个单独的主节点,因为故障转移过后,从节点将扮演主节点的角色,并且旧的主节点被重新配置作为一个从节点,所以需要在全部的Sentinel实例中设置上面的选项,包括主节点和从节点。这通常是一个理智的设置,因为我们不想仅仅在主节点中保护数据,在从节点中也有同样的数据。
在罕见的情况下,需要一个从节点是可进入的且不需要认证,这时可以设置一个从节点的优先级为0(配置参数slave-priority 0),阻止其被提升为主节点,并配置该从节点的masterauth选项而不使用requirepass选项,以便在未认证的情况下数据可以被读取。
4、哨兵细节原理分析
4.1、sdown与odown
Redis Sentinel有两个不同概念的下线:一个被称为主观下线(sdown),一个被称为客观下线(odown)。
- sdown是主观宕机,比如一个哨兵觉得一个主节点宕机了,sdown达成的条件很简单,一个哨兵ping一个主节点,超过了is-master-down-after-milliseconds指定的毫秒数之后就可以主观地认为主节点宕机了。
- odown是客观宕机,比如quorum数量的哨兵都觉得一个主节点宕机了。
sdown到odown转换的条件很简单,如果一个哨兵在指定时间内收到了quorum指定数量的其他哨兵也认为那个主节点是sdown,就可以客观地认为主节点宕机了,即odown。
4.2、哨兵集群的自动发现机制
Sentinel和其他的Sentinel保持连接是为了互相之间检查是否可达和交换消息。然而,我们不需要在每个运行的Sentinel实例中配置其他的Sentinel地址列表,Sentinel使用Redis实例的发布与订阅功能来发现部署中用于监控相同主节点和从节点的其他Sentinel。
通过往名为__sentinel__:hello的通道发送hello消息来实现这个特性。
我们同样不需要配置一个与主节点关联的从节点列表,Sentinel会通过问询Redis自动发现这个列表:
- 每隔两秒,每个Sentinel向每个被监控的主节点和从节点的发布与订阅通道__sentinel__:hello公布一条消息宣布自己的IP地址、端口ID。
- 每个Sentinel都订阅每个主节点和从节点的发布与订阅通道__sentinel__:hello,寻找未知的Sentinel。当新的Sentinel被检测到,就增加为这个主节点的Sentinel。
- hello消息也包含主节点的全部配置信息,如果接收的Sentinel有一个更旧的配置,就会立即更新配置。
- 在为主节添加新的Sentinel之前,Sentinel总是要检查是否已经有一个具有相同ID、地址的Sentinel。在这种情况下,所有匹配的Sentinel会被删除之后,再添加新的Sentinel。
4.3、故障转移的重新配置
即使没有故障转移,Sentinel也将尝试把当前的配置设置到监控的实例上。特别注意如下两点:
- 从节点提升为主节点,将被作为从节点配置来复制当前的主节点。
- 从节点连接了一个错误的主节点,也会被重新配置来复制正确的主节点。
Sentinel重新配置从节点,错误的配置在一段时间内会被观察到,这比广播新的配置方式更好,这样可以阻止过时的配置,比如在一个分区中重新加入的Sentinel在收到更新之前会去交换从节点的配置。需要注意如下事项:
- 主节点在完成故障转移并重回部署之后,会被重新配置作为从节点。
- 在网络分区中,从节点一旦可达(即可用)就被重新配置。
4.4、从节点选举和优先级
当一个Sentinel实例准备执行故障转移时,主节点这时在odown状态下,Sentinel会收到从大多数已知的其他Sentinel实例中授权开始故障转移的消息,于是一个合适的从节点会被选举出来。
从节点选举过程评估下列信息:
- 与主节点断开的时间。
- 从节点的优先级。
- 复制偏移量的处理。
- 运行ID。
一个从节点被发现从主节点断开超过主节点配置时间(down-after-milliseconds选项)10倍以上,加上从正在执行故障转移的Sentinel的角度来看主节点不可用的时间,该从节点将被认为是不合适被选举的。在更为严格的条件下,一个从节点从主节点断开超过以下时长将被认为是不可靠的:
bash
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
选举只会考虑通过了上述测试的从节点,并根据上面的条件进行排序,排序说明如下:
- 根据Redis实例中redis.conf文件内配置的slave-priority进行优先级的排序。
- 如果优先级相同,检查复制偏移量的处理,从主节点收到新数据的从节点会被优先选择。
- 如果多个从节点有相同的优先级和数据偏移量,就执行进一步的检查,选择有更小运行ID的从节点。有一个更小的运行ID并不表示具有真正的优点,只是让从节点的选举更为确定,而不是随机选择一个从节点。
如果按优先级选,那么Redis主节点、从节点都必须配置相应的slave-priority;否则所有的实例都要有一个默认的ID。为了永远不被Sentinel选择为新的主节点,Redis实例可以把slave-priority配置为0,一个这样配置的从节点会被Sentinel重新配置,唯一不同的是它永远不会成为主节点。
4.5、算法和内部结构
Sentinel的使用者并不需要知道Sentinel运行原理的全部细节,不过更为深入的理解有助于我们更加有效地部署和操作Sentinel。
每个被Sentinel监控的主节点与一个配置的quorum相关联,它指定了同意主节点是不可达的或者是错误的所需的Sentinel实例的数量。
在故障转移触发后,为了真正地执行故障转移,大多数的Sentinel必须授权一个Sentinel开始执行故障转移。当只有小部分Sentinel在一个网络分区中,那么故障转移永远不会执行。以下这些流程我们是知道的。
- Quorum:为了把一个主节点标记成odown,需要发现故障的Sentinel实例达到所需的数量。
- odown状态触发故障转移。
- 一旦故障转移被触发,Sentinel尝试向大多数的Sentinel实例请求授权。
如果有5个Sentinel实例,quorum被设置为2,一旦两个Sentinel认为主节点不可达,故障转移就会被触发。然而这两个Sentinel中的一个得到了其他3个Sentinel的授权才会开始执行故障转移。把quorum设置为5,就必须所有这5个Sentinel实例同意主节点失败。为了开始故障转移,需要得到所有Sentinel实例的授权。
这意味着quorum在两方面可以用来调整Sentinel:
- 如果quorum的值被设置为小于我们部署的Sentinel数量太多,就会使Sentinel对主节点的失败更加敏感,并且一旦少数Sentinel不再和主节点交流就会触发故障转移。
- 如果quorum的值被设置为接近我们部署的Sentinel数量,那么仅仅当大多数连接良好的Sentinel同意主节点挂掉时Sentinel才能启动故障转移。
4.6、配置epoch
为了启动故障转移,需要从大多数Sentinel中得到授权,重要原因如下:
- 当一个Sentinel被授权时,它为故障转移的主节点获得一个独一无二的配置epoch。这将用来标识在故障转移完成之后新配置的版本。因为大多数Sentinel同意一个版本被分配给指定的Sentinel,而其他的Sentinel不能使用。这意味着,每次故障转移的配置都有一个独一无二的版本号。
- Sentinel有一个规则:如果一个Sentinel投票给其他的Sentinel,在一次故障转移中,它将等待一段时间再次尝试故障转移这个主节点,可以在sentinel.conf中配置这个延迟时间failover-timeout。这意味着Sentinel在相同的时间内不会尝试故障转移相同的主节点,第一次请求授权的将会尝试,如果失败了,另一个将会在一段时间后尝试,以此类推。
- Redis Sentinel保证了活性(liveness)性质,大多数Sentinel能够交流。如果主节点挂了,最后将有一个节点被授权启动故障转移。
- Redis Sentinel同样也保证了安全(safety)性质,每个Sentinel将使用不同的配置epoch(configuration epoch)来对同一个主节点进行故障转移。
4.7、配置传播
一旦一个Sentinel能成功地对一个主节点执行故障转移,它就将开始广播新的配置,以便其他Sentinel更新它们关于主节点的信息。
为了认定一次故障转移是成功的,需要Sentinel能发送SLAVEOF NO ONE命令给被选举出来的从节点,然后切换为主节点,稍后就能在主节点的INFO输出中观察到。
这时,从节点的重新配置正在进行,故障转移也被认为是成功的,并且所有的Sentinel需要开始报告新的配置。
为新配置采用广播方式的原因是,在每次Sentinel被授权故障转移时有一个不同的版本号。每个Sentinel使用Redis发布与订阅消息来连续不断地广播它的主节点配置的版本号、所有的从节点和主节点。同时,所有的Sentinel等待消息来查看其他的Sentinel广播的配置。
配置在__sentinel__:hello发布与订阅频道中被广播。因为每个配置都有一个不同的版本号,大的版本号总是赢得小的版本号。比如一开始所有的Sentinel认为主节点mymaster的配置为192.168.1.50:6379,这个配置的版本号为1。一段时间后,被授权启动故障转移有了版本号2,如果故障转移成功,它将广播新的配置(192.168.1.50:9000,版本号为2)。所有其他的实例将看到这个配置并更新它们的配置,因为新的配置有更高的版本号。
这意味着Sentinel保证第二个活性属性:一个Sentinel集合能互相交流并且把配置信息收敛到一个更高的版本号。
基本上,如果网络是分区的,那么每个分区将收敛到一个更高的本地配置。在没有网络分区的特殊情况下,只有一个分区,那么每个Sentinel将同意这个本地配置。
4.8、网络分区下的一致性
Redis Sentinel配置最终是一致的,所以每个分区将收敛到更高的可用配置。在使用Sentinel的真实世界系统中,有三个不同的角色:
- Redis实例
- Sentinel实例
- 客户端
为了定义系统的行为,这三种角色我们都考虑。有一个简单的三个节点网络,每个节点中都运行一个Redis实例和一个Sentinel实例,整体架构图如图所示。

在这个系统中,原始状态是Redis-3是主节点,Redis-1和Redis-2是从节点。一个网络分区隔离了旧的主节点。Sentinel-1和Sentinel-2启动故障转移过程,把Sentinel-1提升为新的主节点。
Sentinel的属性保证Sentinel-1和Sentinel-2有了一个主节点的新配置。可是Sentinel-3依然是旧的配置,因为它在一个不同的网络分区中存活。
Sentinel-3将会更新它的配置,当网络分区治愈时,如果有客户端和旧的主节点在一起,网络分区时会发生什么呢?
客户端仍然可以向Redis-3写入数据。当网络分区治愈时,Redis-3变成Redis-1的一个从节点,在网络分区治愈期间写入的数据都会丢失。可以通过修改配置选择是否让这种情况发生:
- 如果使用Redis作为缓存,客户端B仍然可以向旧的主节点写入数据,即使数据将会丢失。
- 如果使用Redis作为存储,这样并不是好的解决办法,只能阻止部分数据的丢失,另外还需要配置系统。
Redis是采用异步复制的,这种情况下没有办法完全阻止数据的丢失,但是可以使用下面的Redis配置选项来限制Redis-3和Redis-1之间的不一致性:
bash
min-slaves-to-write 1
min-slaves-max-lag 10
上面这两个配置可以减少异步复制和脑裂(split-brain)导致的数据丢失,要求至少有1个从节点,数据复制和同步的延迟不能超过10秒。一旦所有从节点的数据复制和同步的延迟都超过了10秒,那么主节点就不会再接收任何命令请求了。
4.8.1、减少异步复制的数据丢失
有了min-slaves-max-lag这个配置选项,就可确保一旦从节点复制数据和确认(ack)延时太长,进而认为主节点宕机后损失的数据太多了,拒绝写请求,这样可以把主节点宕机时由于部分数据未同步到从节点而导致的数据丢失降低到可控的范围内。
4.8.2、减少脑裂的数据丢失
假设一个主节点出现了脑裂,与其他从节点的连接丢失了,上面的两个配置含义是如果不能继续给指定数量的从节点发送数据,而且从节点超过10秒没有给主节点发送确认消息,就直接拒绝客户端的写请求。这样脑裂后旧的主节点就不会接收客户端的新数据,也就避免了数据的丢失。因此,在脑裂场景下,最多丢失10秒的数据。
总之,Redis+Sentinel是一个最终一致性系统(eventually consistent system),即最后一次的故障转移成功(last failover wins)。旧节点中的数据会被丢弃,从当前主节点复制数据,所以总有一个丢失确认写的窗口。这是由Redis的异步复制和系统的"虚拟"合并功能的丢弃性质决定的。注意,Sentinel本身没有限制,如果我们调度好故障转移,那么相同的属性仍然适用,仅有两种方式用于避免丢失写入的确认:
- 使用同步复制。
- 使用一个最终一致的系统,相同对象的不同版本可以被合并。
5、客户端访问哨兵架构的系统
5.1、C#连接Redis哨兵架构的系统
c#
static void Testsentinel(string[] args)
{ // 写入所有哨兵节点的信息
var sentinelHosts = new[] { "192.168.3.204:26379",
"192.168.3.204:26380" };
var sentinel = new RedisSentinel(sentinelHosts);
RedisConfig.DefaultRetryTimeout = 10;
RedisConfig.BackOffMultiplier = 10;
var redisManager = sentinel.Start();
try
{
using (var client = redisManager.GetClient())
{
client.SetValue("Sentinel3Setup", "IntranetSentinel");
var result = client.GetValue("Sentinel3Setup");
}
}
catch (Exception ex)
{
throw;
}
}
5.2、Java连接Redis哨兵架构的系统
xml
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.5.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
代码如下:
java
public static void testSentinel() throws Exception {
String masterName = "mymaster";
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.3.204:26379");
sentinels.add("192.168.3.204:26380");
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels);
Jedis jedis = pool.getResource();
jedis.set("key1", "value1");
pool.close();
}
5.3、客户端原理
如上两种不同语言通过哨兵模式操作Redis。在整个过程中,我们的代码不需要显式地指定主节点的地址就可以连接到主节点;代码中对故障转移也没有任何体现就可以在哨兵完成故障转移后自动切换主节点。之所以可以做到这一点,是因为在Java的JedisSentinelPool和C#的RedisSentinel构造器中都进行了如下两步工作:
- 遍历哨兵节点,获取主节点信息:遍历哨兵节点,通过其中一个哨兵节点+masterName获得主节点的信息,该功能是通过调用哨兵节点的sentinel get-master-addr-by-name命令实现的。一旦获得主节点信息,就停止遍历。
- 增加对哨兵的监听:当发生故障转移时,客户端便可以收到哨兵的通知,从而完成主节点的切换。具体做法是:利用Redis提供的发布与订阅功能为每一个哨兵节点启动一个单独的线程,订阅哨兵节点的+switch-master频道,当收到消息时重新初始化连接池。
需要注意的是,哨兵只是配置提供者,而不是代理程序。二者的区别在于:如果是配置提供者,客户端在通过哨兵获得主节点信息后会直接建立到主节点的连接,后续的请求(如set/get)会直接发向主节点;如果是代理程序,客户端的每一次请求都会发向哨兵,哨兵再通过主节点处理请求。哨兵节点在故障转移完成后会将新的主节点信息发送给客户端,以便客户端及时切换主节点。