Redis之Sentinel

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341