redis之sentinel模式

往期回顾

redis之数据结构与对象篇(一)
redis之数据结构与对象篇(二)
redis之数据结构与对象篇(三)
redis之数据结构与对象篇(终章)
redis之单机数据库杂谈
redis持久化的秘密
redis的复制实现
redis之发布与订阅


Sentinel(哨兵)是Redis的高可用性解决方案:由一个或者多个Sentinel实例组成Sentinel系统可以见识多个主服务器,以及这些主服务器属下的所有从服务器,并在被见识的主服务器进入下线状态时,自动将主服务器属下的从服务器升级为主服务器,然后由新的主从服务器代替已经下线的主服务器继续处理命令请求

下图展示了一个Sentinel系统监视服务器的例子
在这里插入图片描述

  • 双环图案表示的是当前的主服务器server1
  • 单环图表示的是主服务器的三个从服务器server2,server3,server4
  • 三个从服务器正在复制主服务器server1,而Sentinel系统正在监视四个服务器

假设这时,从服务器server1进入下线状态,那么从服务器server2,server3,server4对主服务器的复制操作将被终止,并且Sentinel系统会察觉到server1已经下线在这里插入图片描述
当server1的下线时长超过用户设定的下线时长上线时,Sentinel系统就会对server1执行故障转移操作:

  • 首先,Sentinel系统会挑选server1属下的其中一个从服务器,将这个被选中的从服务器升级为新的主服务器
  • 之后,Sentinel系统会向server1属下的所有从服务器发送新的复制指令,让他们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作完成
  • Sentinel系统会继续监视已经下线的server1,在server1重新上线时,将它设置为新的主服务器的从服务器

启动并初始化Sentinel

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

$ redis-sentinel /path/you/sentinel.conf

当一个Sentinel启动时,它需要执行以下步骤:

  1. 初始化服务器
  2. 将普通的Redis服务器代码替换为Sentinel专有代码
  3. 初始化Sentinel状态
  4. 根据给定的配置文件,初始化Sentinel的监视服务器列表
  5. 创建连向主服务器的网络连接

初始化服务器

Sentinel本质上是一个运行在特殊模式下的Redis服务器,所以启动的第一步就是初始化一个Redis服务器。Sentinel的作用和普通的Redis服务器不同,所以初始化过程也不相同

例如,普通服务器在初始化时会载入RDB文件或者AOF文件来还原数据库状态,但是Sentinel并不使用数据库,所以初始化Sentinel时就不会载入RDB文件或者AOF文件

Sentinel模式下,服务器的主要功能使用情况。

功能使用情况
键值对命令,SET,DEL等不使用
事务命令,MULTI,WATCH不使用
脚本命令不使用
RDB持久化命令不使用
AOF持久化命令不使用

使用Sentinel专有代码

使用Sentinel的第二个步骤就是将一部分普通Redis服务器使用的代码替换成Sentinel专有代码。比如说,普通Redis服务器使用redis.h/REDIS_SERVERPORT 常量的值作为服务器端口号:

#define REDIS_SERVERPORT 6379

而Sentinel则使用sentinel.c/REDIS_SENTINEL_PORT常量的值作为服务器端口:

#define REDIS_SERVERPORT 26379

除此之外,普通REDIS服务器使用redis.c/redisCommandTable作为服务器的命令列表

而Sentinel则使用snetinel.c/sentinelcmds作为服务器命令列表,并且其中的INFO命令在使用Sentinel模式下的专有实现sentinel.c/sentinelInfoCommand函数,而不是普通Redis服务器使用的实现redis.c/infoCommand函数

sentinelcmds命令表解释了Sentinel模式下,redis服务器不能执行SET,DEL等命令,因为服务器根本没有在命令表中载入这些命令。

PING,SENTINEL,INFO,SUBSCRIBE,UNSUBSCRIBE,PSUBSCRIBE,PUNSUBSCRIBE这七个命令就是客户端对Sentinel执行的全部命令

初始化Sentinel状态

在应用了Sentinel专有代码之后,接下来服务器会初始化一个Sentinel.c/sentinelState结构,这个结构保存了服务器中所有和Sentinel功能有关的状态

struct sentinelState{
	uint64_t current_epocj ;
	//保存所有被这个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;

初始化Sentinel状态的masters属性

Sentinel状态中的masters字典记录了所有被Sentinel监视的主服务器的相关信息,其中:

  • 字典的键时被监视主服务器的名字
  • 字典的值是被监视主服务器对应的sentinelRedisInstance结构。每个sentinelRedisInstance结构代表一个被Sentinel监视的Redis服务器实例,这个实例可以是主服务器,从服务器,或者另一个Sentinel

对Sentinel状态的初始化将引发对masters字典的初始化,而nasters字典的初始化根据被载入的Sentinel配置文件来进行

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

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

对于每个被Sentienl监视的主服务器,Sentinel会创建两个连向主服务器的连接:

  • 一个是命令连接,这个连接专门用于向主服务器发送命令,并且接受命令回复
  • 另外一个是订阅连接,这个连接专门用于订阅主服务器的_sentinel_:hello频道
    在这里插入图片描述

获取主服务器的信息和从服务器信息

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

例如,主服务器master有三个从服务器slave0,slave1,slave2,那么Sentinel将持续向主服务器发送INFO命令
在这里插入图片描述
通过分析主服务器返回的INGO命令回复,Sentinel可以获取以下两方面信息:

  • 一方面是关于主服务器本身的信息,包括run_id记录的服务器运行ID,以及role记录的服务器角色
  • 另一方面是关于主服务器属下所有从服务器的信息,每个从服务器都由一个slave字符串开头的行几率,每行的ip记录了从服务器的IP地址,而port记录了从服务器的端口号。根据这些IP地址和端口号,Sentienl可以自动发现从服务器

根据run_id和role记录的信息,Sentinel将对主服务器的实例结构进行更新,例如,主服务器启动后,它的运行id不同,Sentinl检测到这一情况后,会对实例结构的运行ID进行更新

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

  • 字典的键由Sentinel自动设置从服务器的名字,格式为ip:port:比如ip为127.0.0.1,端口为11111的从服务器来说,Sentinel为它设置的名字就是127.0.0.1:11111
  • 字典的值则是从服务器对应的实例结构

Sentinel在分析INFO命令中包含从服务器信息,会检查从服务器的实例结构是否存在slaves字典中

  • 如果从服务器对应的实例结构已经存在,那么Sentinel对从服务器的实例结构进行更新
  • 如果从服务器对应的实例结构不存在,那么说明这个从服务器时新发现的从服务器,Sentinel会在slaves字典中为这个从服务器创建一个实例结构

主服务器实例结构和从服务器实例结构之间的区别

  • 主服务器实例结构的flags属性的值为SRI_MASTER,而从服务器实例结构的flags属性的值为SRI_SLAVE
  • 主服务器实例结构的name属性的值是用户使用Sentinel配置文件设置的,而从服务器实例结构的name属性的值则是Sentinel根据从服务器的IP地址和端口号自动设置的

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

在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送以下格式的命令:

PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

这条命令向服务器的_sentinel_:hello频道发送一条消息,信息的内容由多个参数组成

  • 其中以s开头的参数记录了Sentinel本身的信息
  • 以m开头的参数记录的是主服务器的信息。如果Sentinel正在监视的是主服务器,那么这些参数记录的就是主服务器信息里如果Sentinel正在监视的是从服务器,那么这些参数记录的就是从服务器正在复制的主服务器信息

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

当sentinel与一个主服务器或者从服务器建立起订阅连接之后,Sentinel会通过订阅连接,向服务器发送以下命令

SUBSCRIBE ——sentinel_:hello

Sentinel对此频道的订阅会一致持续到Sentinel与服务器的连接断开为止。

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

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

当一个Sentienal从_sentinel_:hello频道收到一条信息时,Sentinel会对这条信息进行分析,提取出信息中的Sentinel IP和端口号,进行以下检查

  • 如果信息中记录的Sentinel运行ID和接受信息的Sentienl的运行ID相同,那么说明这条信息是Sentinel自己发送的,Sentinel将丢弃这条信息
  • 如果Sentinel运行ID和接受信息的Sentinel的运行ID不相同,那么说明这条信息是监视同一个服务器的其他Sentinel发来的,接受信息的Sentinel将根据信息中的各个参数,对相应的主服务器实例结构进行更新

更新sentinels字典

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

  • sentinel字典的键为Sentinel的名字,格式为ip:port,比如对于IP地址为127.0.0.1,端口号为26379的Sentinel来说,这个Sentinel在sentinels字典中的键就是127.0.0.1:26379
  • sentinels字典的值则是键所对应Sentinel字典中的值就是IP为127.0.0.1,端口号为26379的Sentinel的实例结构

当一个Sentinel接受到其他Sentinel发来的消息时,目标Sentinel会从信息中分析并提取出对应的参数:

  • sentinel有关的参数:源Sentinel的IP地址,端口号,运行ID和配置纪元
  • 与主服务器有关的参数:源Sentinel正在监视的主服务器的名字,IP地址,端口号与配置纪元

根据信息中提取出来的主服务器参数,目标Sentinel会在自己的Sentinel状态的masters字典中查找相应的主服务器实例结构,然后根据提取出来的Sentinel参数,检查主服务器实例结构中的sentinels字典,源Sentinel是否已经存在:

  • 如果源Sentinel实例结构存在,那么对源Sentinel的实例结构进行更新
  • 如果源Sentinel实例结构不存在,那么说明源Sentinel是刚刚开始监视主服务器的Sentinel,目标Sentinel会为源Sentinel创建一个新的实例结构,并将这个结构添加到sentinels字典中

检查主观下线状态

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

在这里插入图片描述

  • Sentinel1将向Sentinel2,主服务器server1和从服务器slave1和slave2发送PING命令
  • Sentinel2将向Sentinel,主服务器master,从服务器slave1和slave2发送PING命令

实例对PING命令回复可以分为以下两种情况:

  • 有效回复:实例返回+PONG,-LOADING,-MASTERDOWN三种回复的其中一种
  • 无效回复:实例返回除+PONG,-LOADING,-MASTERDOWN三种回复之外的其他回复,或者在指定时限内没有返回任何回复

Sentinel配置文件中制定了Sentinel判断实例进入主观下线需要的时间长度:如果一个实例在规定时间(down_afer_millseconds)毫秒内,连续向Sentinel返回无效回复,那么Sentinel会修改这个实例所对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,以此来表示这个实例进入主观下线状态

检查客观下线状态

当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel进行询问,看他们是否也认为主服务器已经进入下线状态。当Sentinel从其他Sentinel那里接受到足够数量的已下线判断后,Sentinel就会将从服务器判断为客观下线状态,并且对主服务器执行故障转移操作

选举leader Sentinel

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

故障转移

在选举产生出零头Sentinel之后,领头Sentinel对已下线的主服务器执行故障转移操作,该操作包含以下三个步骤:

  1. 在已下线主服务器的从服务器里面挑选出一个从服务器,并且将其转换为主服务器
  2. 让已下线主服务器属下的所有从服务器改为复制新的主服务器
  3. 将已经下线的主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器

选出新的主服务器

故障转移操作第一步要做的就是在已经下线的master的slaves中,挑选一个状态良好,数据完整的从服务器,然后向从服务器发送SLAVEOF no one命令,将这个从服务器转换为主服务器

在这里插入图片描述
上图展示在一次故障转移操作中,领头Sentinel向被选中的从服务器server2发送SLAVEOF no one命令

在发送SLAVEOF no one命令之后,领头sentinel以每秒一次的频率向升级的从服务器发送INFO信息,当被升级服务器的role从原来的slave变为master时,领头Sentinel就知道被选中的从服务器已经顺利升级为主服务器了

修改从服务器的复制目标

当新的主服务器出现后,领头Sentinel下一步就是,让已经下线的主服务器下的从服务器去复制新的主服务器,这一动作可以通过向从服务器发送SLAVEOF命令来实现

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

故障转移操作最后要做的就是,将已经下线的主服务器设置为新的主服务器的从服务器。

下图展示了旧的主服务器下线,当server1重新上线时,Sentinel向它发送SLAVEOF命令,让它成为server2的从服务器

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值