Redis 哨兵模式的理论(转载)

Sentinel是Redis的高可用性解决方案,本文主要介绍Sentinel的初始化过程及其与一般Redis服务器的区别。并说明Sentinel监视服务器的方法和原理,说明Sentinel如何判断一个服务器是否在线,并介绍故障转移过程。

I、上帝视角看Sentinel

由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器替代已下线的主服务器继续处理命令请求。

Sentinel的主要功能就是监视主服务器是否下线,并进行相应处理(故障转移)。
下面四个示意图展示了Sentinel系统监视服务器过程:

1、server2、server3、server4 三个从服务器正在复制主服务器server1,而Sentinel系统监视这四个服务器:

 

2、此时,server1下线,从服务器会中止其复制状态:

 

3、当Sentinel系统判断server1的下线时长超过下线时长上限时,会为server1执行故障转移操作:
· 首先,Sentinel系统会挑选server1属下的其中一个从服务器(后面会介绍选择标准),将其升级为新的主服务器;
· 然后,Sentinel系统会向server1属下的所有从服务器发送新的复制指令,让它们成为新的主服务器(以server2为例)的从服务器;
· Sentinel系统还会继续监视已经下线的server1,并在它重新上线时,将其设置为server2的从服务器;

II、启动并初始化Sentinel

可以使用如下命令启动一个Sentinel:

$ redis-sentinel /path/to/your/sentinel.conf
或者
$ redis-server /path/to/your/sentinel.conf --sentinel

这时Sentinel系统会执行以下操作:
· 初始化服务器;
· 将普通Redis服务器使用的代码替换成Sentinel专用代码;
· 初始化Sentinel状态;
· 根据给定的配置文件,初始化Sentinel的监视主服务器列表;
· 创建连向主服务器的网络连接;

2.1 初始化服务器

Sentinel本质上只是一个运行在*特殊模式下8的Redis服务器,所以启动Sentinel的第一步就是启动一个普通的Redis服务器,具体步骤如Redis之服务器
但是,Sentinel的工作与普通Redis有有所不同,所以在初始化过程中并不与初始化Redis服务器完全相同,如Sentinel不需要通过载入RDB文件或AOF文件还原数据库状态。

Sentinel与Redis普通服务器在使用功能上的主要不同:

 

2.2 使用Sentinel专用代码

普通Redis服务器使用redis.c/redisCommandTable作为服务器命令表;
而Sentinel使用sentinel.c/sentinelcmds作为服务器的命令表;

PINGSENTINELINFOSUBSCRIBEUNSUBSCRIBEPSUBSCRIBEPUNSUBSCRIBE这七个命令为客户端可以对Sentinel执行的全部命令。

2.3 初始化Sentinel状态

服务器初始化一个sentinel.c/sentinelState结构,这个结构保存了服务器中所有和Sentinel功能有关的状态(而服务器的一般状态由redis.h/redisServer结构保存):

struct sentinelState {
    //当前纪元,用于实现故障转移
    uint64_t current_epoch;

    //保存所有被这个sentinel监视的主服务器
    //字典的键时主服务器的名字
    //字典的值则是一个指向sentinelRedisInstance结构的指针
    dict *masters;

    //是否进入TILT模式
    int tilt;

    //目前正在执行的脚本数量
    int running_scripts;

    //进入TITL模式的时间
    mstime_t tilt_start_time;

    //最后一次执行时间处理器的时间
    mstime_t previous_time;

    //一个FIFO队列,包含了所有需要执行的用户脚本
    list *scripts_queue;
} sentinel;

2.4 初始化Sentinel状态的masters属性

Sentinel状态中的masters字典记录了所有被Sentinel监视的主服务器的相关信息,其中:
· 字典的键为被监视主服务器的名字;
· 字典的值为主服务器对应的sentinel.c/sentinelRedisInstance结构,这个结构代表一个被Sentinel监视的Redis服务器实例,这个实例可以是主服务器、从服务器或者另一个Sentinel。

下图说明了一个masters字典结构:

 

下图说明了一个sentinelRedisInstance结构:

 

· addr属性是一个指向sentinel.c/sentinelAddr结构的指针,这个结构保存着实例的IP地址和端口号。

2.5 创建连向主服务器的网络连接

初始化Sentinel的最后一步是创建连向监视主服务器的网络连接,Sentinel将成为主服务器的客户端,它可以向主服务器发送命令,并从命令回复中获取相关信息。

对于每个Sentinel监视的主服务器来说,Sentinel会创建两个连向主服务器的异步网络连接
· 一个是命令连接
· 一个是订阅连接,这个连接专门用于订阅主服务器的__sentinel__:hello频道。这是因为Redis的发布与订阅功能中,被发送的信息不会保存在Redis服务器里面,如果在信息发送时,想要接收信息的客户端不在线,则这个客户端就会丢失这条信息。因此,为了不丢失__sentinel__:hello 频道的任何信息,Sentinel专门用一个订阅连接来接收该频道的信息。

下图展示了一个Sentinel向它监视的两个master创建网络连接的实例:

 

III、获取主服务器信息

Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复获取主服务器的当前信息。

通过分析主服务器返回的回复,Sentinel可以获取两方面的信息:
· 一方面是关于主服务器本身的信息,包括run_id域记录的服务器运行ID,以及role域记录的服务器角色。
· 另一方面会得到主服务器属下的所有从服务器的信息(主要包括从服务器的IP::port)。

主服务器返回的从服务器信息,会被用于更新主服务器实例结构的slaves字典,这个字典记录了主服务器属下从服务器的名单:

字典结构如下图所示:

 

IV、获取从服务器信息

Sentinel除了会为从服务器创建相应的实例结构外,还会创建连接到从服务器的命令连接和订阅连接

接下来,Sentinel会以每十秒一次的频率通过命令连接向从服务器发送INFO命令,并获得相应的回复,这些回复包括:
· 从服务器运行ID run_id;
· 从服务器的角色 role;
· 主服务器的IP地址master_host,以及主服务器的端口号 master_port;
· 从服务器的优先级 slave_priority;
· 从服务器的复制偏移量 slave_repl_offset;

根据以上信息,Sentinel会对从服务器的实例结构进行更新:

 

V、向主服务器和从服务器发送信息

默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送命令:
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

这条命令向服务器的__sentinel__:hello频道发送了一条信息,信息内容含义如下图:

 

 

VI、接收来自主服务器和从服务器的频道信息

当Sentinel与一个主服务器或从服务器建立起订阅连接之后,Sentinel会通过订阅连接,向服务器发送以下命令:
SUBSCRIBE __sentinel__:hello
也就是Sentinel对所有服务器的__sentinel__:hello频道进行了订阅,这个订阅会一直持续到连接断开。

也就是说,对于每个与Sentinel连接的服务器,**Sentinel既通过命令连接向服务器的__sentinel__:hello频道发送信息,又通过订阅连接从服务器的sentinel__:hello频道接受信息:

 

可见,命令连接用于向服务器发送命令请求,而订阅连接用于接收指定频道的信息。

举个例子:
假设现在有sentinel1、sentinel2、sentinel3三个Sentinel在监视同一个服务器,那么当sentinel1向服务器的__sentinel__:hello频道发送一条信息时(使用命令连接),所有订阅了__sentinel__:hello频道的Sentinel都会收到这条信息:

 

之后Sentinel会对收到的订阅信息进行分析,提取出V中说明的八个参数,并进行检查:
· 如果信息中的Sentinel运行ID与自己的运行ID相同,说明这条信息是Sentinel自己发送的,Sentinel将丢弃这条信息,不做进一步处理。
· 如果不同,说明这条信息是监视同一个服务器的其他Sentinel发来的(这里要理解订阅信息是由谁发送的,订阅信息不是由服务器自己产生的,而是有客户端发送到服务器的,这里的客户端即是各个Sentinel),接受信息的Sentinel将根据信息中的各个参数,对相应主服务器的实例结构进行更新。

6.1 更新sentinels字典

Sentinel为主服务器创建的实例结构中的sentinels字典保存了除Sentinel本身之外,同样监视这个主服务器的其他Sentinel的资料:

 

而这些资料是由于所有监视某个主服务器的所有Sentinel都订阅了主服务器的__sentinel__:hello频道,当Sentinel自己通过命令连接向主服务器发送如V中描述的PUBLISH命令时,会通过主服务器的订阅连接广播给其他Sentinel。于是监视同一个主服务器的多个Sentinel可以自动发现对方

6.2 创建连向其他Sentinel的命令连接

当Sentinel通过频道信息发现一个新的Sentinel时,不仅会为其在sentinels字典重创建实例,还会创建一个连向新Sentinel的命令连接,从而形成一个如下图的结构:

Sentinel之间的命令连接用来进行信息交换,实现如主观下线检测客观下线检测等功能。

VII、检测主观下线状态

默认情况下,Sentinel会以每秒一次的频率向与它创建命令连接的实例(包括主服务器,从服务器,其他Sentinel)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。

实例对PING命令的回复可以分为一下两种情况:
· 有效回复: +PONG-LOADING-MASTERDOWN
· 无效回复: 上面三种回复之外的其他回复,或在指定时限内没有任何回复。

如果一个实例在配置的down-after-milliseconds时间内,连续向Sentinel返回无效回复,那么Sentinel会修改这个实例对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,以此来表示这个实例已进入主观下线状态

这里对于每个Sentinel给相同服务器的down-after-milliseconds配置可能是不同的,所以当一个Sentinel将主服务器判断为主观下线时,其他Sentinel可能仍然会认为这个主服务器处于在线状态

VIII、 检查客观下线状态

当Sentinel将一个主服务器判断为主观下线后,为了确认这个主服务器是否真的下线了,会向同样监视这一主服务器的其他Sentinel进行询问,看它们是否也认为主服务器已经进入了下线状态(可能是主观下线或者客观下线)。当Sentinel从其他Sentinel得到足够多数量的下线判断后,Sentinel会将主服务器判定为客观下线,并对主服务器执行故障转移操作

Sentinel使用SENTINEL is-master-down-by-addr命令来与其他Sentinel交互,判断主服务器是否客观下线,如果达到配置的客观下线所需的数量时,Sentinel打开主服务器结构中的flags属性的SRI_O_DOWN标识,标识主服务器进入客观下线状态。

IX、选举领头Sentinel

当一个主服务器被判断为客观下线后,检测这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线服务器执行故障转移操作。

选取领头Sentinel的规则和方法为:

 

X、故障转移

领头Sentinel对已下线的主服务器进行故障转移操作的步骤如下:
1、在已下线的主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器;
2、让已下线主服务器属下的所有从服务器改为赋值新的主服务器;
3、将已下线主服务器设置为新的主服务器的从服务器。

10.1 选出新的主服务器

1、新的主服务器的挑选过程如下:

 

2、实例:
假设根据以上规则挑选出server2为新的主服务器,则Sentinel向server2发送SLAVEOF no one命令;

 

在发送完SLAVEOF no one命令之后,领头Sentinel以每秒一次的频率(平时是每十秒一次)向server2发送INFO命令,观察其role信息,当server2的role由slave变为master时,则server2顺利升级为主服务器。

10.2 修改从服务器的复制目标

领头Sentinel向server3和server4发送SLAVEOF命令,令其复制新的主服务器server2的内容:

形成如下结构:

 

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

当server1重新上线时,Sentinel向其发送SLAVEOF命令,让其成为server2的从服务器:

image.png

【参考】
[1] 《Redis设计与实现》

欢迎转载,转载请注明出处wenmingxing Redis之Sentinel



作者:wenmingxing
链接:https://www.jianshu.com/p/bd777dc24dc0
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值