0%

Redis中的哨兵机制

哨兵是Redis高可用的解决方案,由Sentinel实例组成的哨兵系统可以监视主服务器和从服务器。当被监视的主服务器处于下线状态时,自动将某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器处理命令请求。

启动Sentinel节点

使用如下的命令可以启动Sentinel节点。其中./sentinel.conf代表sentinel的配置文件位置。

1
2
3
redis-sentinel ./sentinel.conf
#
redis-server ./sentinel.conf --sentinel

当一个Sentinel节点启动时,会执行以下的步骤:

初始化服务器

Redis的Sentinel节点本质就是一个Redis服务器,启动Sentinel节点的第一步就是初始化一个Redis服务器。与普通的Redis服务器不同,Sentinel节点不会载入RDB和AOF文件。初始化完成以后,将普通服务器的代码替换成为Sentinel节点的代码,替换的部分包括服务器运行的端口、可执行的命令表等等。

初始化sentinel状态

接下来,服务器会初始化一个sentinelState结构,保存所有和Sentinel功能相关的状态。其结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct sentinelState {
// 当前纪元
uint64_t current_epoch;
// 保存了所有被这个 sentinel 监视的主服务器
// 字典的键是主服务器的名字
// 字典的值则是一个指向 sentinelRedisInstance 结构的指针
dict *masters;
// 是否进入了 TILT 模式?
int tilt;
// 目前正在执行的脚本的数量
int running_scripts;
// 进入 TILT 模式的时间
mstime_t tilt_start_time;
// 最后一次执行时间处理器的时间
mstime_t previous_time;
// 一个 FIFO 队列,包含了所有需要执行的用户脚本
list *scripts_queue;
} sentinel;

其中masters字典保存了所有所有被Sentinel节点监视的主服务器的相关信息。其键为监视主服务器的名字,值是一个sentinelRedisInstance结构的指针,代表一个被Sentinel节点监视的Redis服务器实例,可以是主服务器、从服务器或者另外一个Sentinel节点。sentinelRedisInstance结构中会保存实例的IP地址、端口号、实例运行ID等信息。

创建网络连接

初始化Sentinel节点的最后一步就是创建连向被监视主服务器的网络连接。

对于每个被监视的主服务器来说,Sentinel节点会创建两个连向主服务器的异步网络连接。

  • 命令连接:用于向主服务发送命令,并接受命令回复
  • 订阅连接:用于订阅主服务器的__sentine__:hello频道

通过这两个连接,就可以来与被监视主服务器进行通信。

获取服务器信息

获取主服务器信息

Sentinel节点会以每10秒1次的频率,向被监视的主服务器发送INFO信息,通过主服务器回复Sentinel节点可以获取主服务器本身的信息和主服务器属下所有从服务器的信息。通过主服务器传递的从服务器信息,Sentinel无需用户提供服务器的地址信息就可以自动发现从服务器。

根据主服务器传递的信息,Sentinel会对主服务器的实例结构进行更新。例如主服务器重启以后,运行ID就会和之前保存的ID不同,就需要更新运行ID信息。

获取从服务器信息

Sentinel节点在分析INFO命令中的从服务器信息时,会检查从服务器的实例结构是否已经存在于slaves字典之中。如果对应的实例结构已经存在,那么就对已保存的实例结构进行更新。否则说明这个从服务器是新发现的从服务器,会在slaves字典之中为从服务器创建一个新的实例结构。

当Sentinel发现有新的从服务器出现时,除了会创建对应的实例结构外,还会创建到从服务器的命令连接以及订阅连接。在创建连接之后,Sentinel会以每10秒1次的频率,向从服务器发送INFO命令。根据INFO命令的回复,Sentinel会获得以下的信息:

  • 从服务器运行ID
  • 从服务器角色
  • 主服务器IP和端口号
  • 主从服务器连接状态
  • 从服务器优先级
  • 从服务器的复制偏移量

发送与接受信息

Sentinel会以两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送一条信息,信息的内容包括:

  • Sentinel的IP地址
  • Sentinel的端口号
  • Sentinel的运行ID
  • Sentinel的配置纪元
  • 主服务器的名字
  • 主服务器的IP地址
  • 主服务器的端口号
  • 主服务器当前的配置纪元

当Sentinel与服务器建立起订阅连接以后,Sentinel会通过订阅连接,向服务器发送以下命令

1
SUBSCRIBE __sentinel__:hello

对于监视一个同服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,用于更新其他Sentinel对于发送信息Sentinel的认知和其他Senttinel对于被监视服务器的认知。

例如sentinel1、sentinel2、sentinel3都在监视同一个服务器,当sentinel1向服务器的__sentinel__:hello频道发送一条信息时,所有的sentinel都会收到这条信息。

当Sentinel从__sentinel__:hello频道接收到一条信息时,会对该信息进行分析,提取从中的Sentinel的运行ID,如果与自己的运行ID相同,说明是自己发送的,不做进一步处理。否则说明是其他的Sentinel发送的,更新信息中的各个参数。如果在此过程中发现了新的Sentinel节点,会创建连向其他Sentinel的命令连接,使得最终监视同一服务器的Sentinel形成相互连接的网络。

检测下线状态

Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器,从服务器,Sentinel节点)发送PING命令。通过PING命令的回复来判断实例是否在线。如果一个实例在一定时间内连续向Sentinel返回无效回复,那么会将该实例状态设置为主观下线状态。

当Sentinel将一个主服务器判断为主观下线之后,为了确认主服务是否真的下线,同样会对监视这一主服务器的Sentinel进行询问。如果从其他Sentinel中接收到足够多的下线判断后,会将服务器判定为客观下线,对主服务器进行故障转移操作。

选举领头

当一个主服务器被判断为客观下线之后,监视这个下线服务器的Sentinel会进行协商,选举出一个领头Sentinel。由该领头Sentinel对下线服务器进行故障转移操作。选举领头的方式采用的是Raft算法。

故障转移

在选举出领头Sentinel之后,领头Sentinel会对已下线的主服务器进行故障转移操作,其步骤如下:

选出主服务器

在主服务器下属的的所有从服务器中,选择出一个状态良好并且数据完整的从服务器,向这个从服务器发送SLAVE no one命令,将从服务器转换为主服务器。在发送完成以后,领头Sentinel会向被升级的从服务器发送INFO命令,观察其角色信息。如果返回的角色信息变为了master,则进行下一步操作。

修改从服务器的复制目标

当新的主服务器出现以后,通过向所有的从服务器发送SLAVEOF命令,让所有的从服务器去复制新的主服务器。

将旧的主服务器变为从服务器

当旧的主服务器重新上线之后,Sentinel会向其发送SLAVEOF命令,让它成为新的主服务器的从服务器。