Redis Sentinel

[toc]
Sentinel是Redis的高可用解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器下线时,自动将下线主服务器属下的某个从服务器升级为主服务器,代替下线的主服务器继续处理命令请求。若已下线的主服务器重新上线,则将它设置为新的主服务器的从服务器。

启动并初始化一个Sentinel

  1. 启动命令
    $ redis-sentinel /path/to/your/sentinel.conf
或  $ redis-server /path/to/your/sentinel.conf --sentinel
  1. 初始化服务器
    Sentinel本质上是一个运行在特殊模式下的Redis服务器,不过功能不一样:

    image

  2. Sentinel专用代码
    Sentinel模式下的Redis服务器使用专用的代码,只支持PING、SENTINEL、INFO、SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE、PUNSUBSCRIBE七个命令:

// 服务器在 sentinel 模式下可执行的命令
struct redisCommand sentinelcmds[] = {
    {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},
    {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0},
    {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0},
    {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
    {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0},
    {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
    {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0},
    {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0},
    {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}
};
  1. Sentinel状态
    服务器会初始化sentinelState结构,其中的字典masters记录了该sentinel监视的主服务器相关信息,字典的键是主服务器的名字,值是一个sentinelRedisInstance结构,包含很多属性,其中sentinelAddr是一个保存IP地址和端口号的结构。
/* Sentinel 的状态结构 */
struct sentinelState {
    uint64_t current_epoch;   // 当前纪元
    dict *masters; 
    // 保存了所有被这个 sentinel 监视的主服务器
    // 字典的键是主服务器的名字
    // 字典的值则是一个指向 sentinelRedisInstance 结构的指针

    ... ...
} sentinel;

// Sentinel 会为每个被监视的 Redis 实例创建相应的 sentinelRedisInstance 实例
// (被监视的实例可以是主服务器、从服务器、或者其他 Sentinel )
typedef struct sentinelRedisInstance {
    int flags;    // 标识值,记录了实例的类型,以及该实例的当前状态
    char *name;   
    // 实例的名字
    // 主服务器的名字由用户在配置文件中设置
    // 从服务器以及 Sentinel 的名字由 Sentinel 自动设置
    // 格式为 ip:port ,例如 "127.0.0.1:26379"
    char *runid;    // 实例的运行 ID
    sentinelAddr *addr;   // 实例的地址
    dict *sentinels;   // 其他同样监控这个主服务器的所有 sentinel
    dict *slaves;  
    // 如果这个实例代表的是一个主服务器
    // 那么这个字典保存着主服务器属下的从服务器
    // 字典的键是从服务器的名字,字典的值是从服务器对应的 sentinelRedisInstance 结构
    ... ...
} sentinelRedisInstance;

/* 地址对象,用于保存 IP 地址和端口 */
typedef struct sentinelAddr {
    char *ip;
    int port;
} sentinelAddr;

假设一个sentinel监听两个主服务器,结果是:


image
image
  1. 创建连接主服务器的网络连接
    对于每个主服务器,Sentinel创建两个连接主服务器的异步网络连接:
  • 命令连接,用于向主服务器发送命令并接收命令回复;
  • 订阅连接,用于订阅主服务器的 sentinel:hello频道。
  1. 获取主服务器信息
    Sentinel默认每10秒向监视的主服务器发送INFO命令:得到主服务器以及其下属从服务器的信息:
    image

根据返回的信息可以更新或创建sentinelRedisInstance实例结构中的信息,并创建连接到从服务器的命令连接和订阅连接:


image
  1. 获取从服务器信息
    Sentinel每10s向从服务器发送INFO命令,获得从服务器运行ID、角色、其主服务器IP地址和端口、连接状态、优先级、复制偏移量等信息,更新从服务器的实力结构:

    image

  2. 频道信息
    Sentinel每2s会通过命令连接向所有监视的主服务器和从服务器发送命令:

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

该命令向服务器的sentinel:hello频道发送一条信息,其他订阅该频道的Sentinel会通过该频道接收到消息:

image

如果Sentinel收到的信息是自己发送的,则忽略;否则根据信息更新实例结构中的sentinels字典:


image

这样就能发现监视同一个主服务器的其他Sentinel了,并与其他Sentinel建立命令连接,形成网络。

下线

Sentienl默认每秒向创建了命令连接的实例(包括主服务器、从服务器和其他Sentinel)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。

  • 若回复是+PING、-LOADING、-MASTERDOWN,则有效;
  • 否则就是无效回复。

若连续一段时间(down_after_milliseconds)内返回的是无效回复,则将Sentinel状态中的对应实例结构的flags属性设置为主观下线(SRI_S_DOWN)。

不同Sentinel对down_after_milliseconds设置可能不同,因此不同Setinel对某个服务器是否主观下线的判断可能不同。

当Sentinel判断一个主服务器为主观下线后,会询问其他Sentinel该主服务器的状态,若超过一定数量的Sentinel判断其为主观下线,则将该主服务器判定为客观下线,并对其执行故障转移操作。

  1. 询问
    发送以下命令进行询问:
SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>
image
  1. 目标Sentinel接收询问命令
    目标Sentinel接收到询问命令,则根据询问命令的IP和端口号,检查对应主服务器的状态,然后回复:
  • <down_state>
  • <leader_runid>
  • <leader_epoch>


    image
  1. 接收询问命令回复
    统计同意主服务器已下线的Sentinel数量,若超过某个数值,则则判定该主服务器为客观下线(SRI_O_DOWN)。
    不同Sentinel判断客观下线的条件(数量值)可能不同。

选举领头Sentinel

当一个主服务器被判定为客观下线时,监视这个主服务器的Sentienl会协商出一个领头Sentinel,并由领头Sentinel对下线主服务器进行故障转移操作。

领头Sentinel选举规则和方法:

  • 所有在线的Sentinel都有被选为领头Sentinel的资格,换句话说,监视同一个主服 务器的多个在线Sentinel中的任意一个都有可能成为领头Sentinel。
  • 每次进行领头Sentinel选举之后,不论选举是否成功,所有Sentinel的配置纪元(configuration epoch)的值都会自增一次。配置纪元实际上就是一个计数器,并没有什么特别的。
  • 在一个配置纪元里面,所有Sentinel都有一次将某个Sentinel设置为局部领头 Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改。 每个发现主服务器进人客现下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel。
  • 当一个Sentinel(源Sentinel)向另一个 Sentinel (目标Sentinel)发送SENTINEL is-master-down-by-addr命令,并且命令中的runid参数不是*符号而是源Sentinel的运行ID时,这表示源Sentinel要求目标Sentinel将前者设置为后者的局 部 领 头 Sentinel。
  • Sentinel设置局部领头Sentinel的规则是先到先得:最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,而之后接收到的所有设置 要求都会被目标Sentinel拒绝。
  • 目标Sentinel在接收到SENTINEL is-master-down-by-addr命令之后,将向 源Sentinel返回一条命令回复,回复中的leader_runid参数和leader_epoch 参数分別记录了目标Sentinel的局部领头Sentinel运行ID和配置纪元。
  • 源Sentinel在接收到目标Sentinel返回的命令回复之后,会检查回复中leadeir_ epoch参败的值和自己的配置纪元是否相同,如果相同的话,那么源Sentinel继续取出回复中的leader_runid参数,如果leader_runid参数的值和源Sentinel的运行ID 一致,那么表示目标Sentinel将源Sentine设置成了局部领头Sentinel。
  • 如果有某个Sentinel被半数以上的Sentinel设置成了局部领头Sentinel, 那 么 这 个Sentinel成为领头Sentinel。举个例子,在一个由10个Sentinel组成的Sentinel系统里面,只要有大于等于10/2+1=6个Sentinel将某个Sentinel设置为局部领头Sentinel,那么被设置的某个Sentinel就会成为领头Sentinel。
  • 因为领头Sentinel的产生需要半数以上Sentinel的支持,并且每个Sentinel在每个 配置纪元里面只能设置一次局部领头Sentinel,所以在一个配置纪元里面,只会出现 一个领头 Sentinel。
  • 如果在给定时限内,没有一个Sentinel被选举为领头Sentinel, 那么各个Sentinel将在一段时间之后再次进行选举,直到选出领头Sentinel为止。

故障转移

领头Sentinel将对已下线的主服务器执行故障转移操作,执行以下三个步骤:

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

挑选新的主服务器规则:
领头Sentinel会将己下线主服务器的所有从服务器保存到一个列表里面,然后按以下规則,一项一项地对列表进行过滤:

  1. 删除列表中所有处于下线或者断线状态的从服务器,这可以保证列表中剩余的从服务器都是正常在线的;
  2. 删除列表中所有最近五秒内没有回复过领头Sentinel的INFO命令的从服务器,这可以保证列表中剩余的从服务器都是最近成功进行过通信的。
  3. 删除所有与已下线主服务器连接断开建过down-after-milliseconds10毫秒的从服务器 : down- after-milliseconds选项指定了判断主服务器下线所需的时间,而删除断开时长超过down-after-milliseconds10毫秒的从服务器, 则可以保证列表中剩余的从服务器保存的数据都是比较新的。

之后,领头Sentinel将根据从服务器的优先级,对列表中剩余的从服务器进行排序,并选出其中优先級最高的从服务器。
如 果 有 多 个 具 有 相 同 最 高 优 先级 的 从服务器,那么领头Sentinel将按从服务器的复制偏移量,对具有相同最高优先级的所有从服务器进行排序,并选出其中偏移量最大的从服务器 (复制偏移量最大的从服务器就是保存着最新数据的从服务器)。

最后,如果有多个优先级最高、复制偏移量最大的从服务器,那么领头Sentinel将按照运行ID对这些从服务器进行排序,并选出其中运行ID最小的从服务器。

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

推荐阅读更多精彩内容