《Redis设计与实现》读书笔记(二十六) ——Redis哨兵(sentinel)启动与建立监听机制

时间:2022-05-03
本文章向大家介绍《Redis设计与实现》读书笔记(二十六) ——Redis哨兵(sentinel)启动与建立监听机制,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

《Redis设计与实现》读书笔记(二十六) ——Redis哨兵(sentinel)启动与建立监听机制

(原创内容,转载请注明来源,谢谢)

一、概述

哨兵(Sentinel)是redis高可用性的解决方案,由一个或多个哨兵实例组成的哨兵系统,可以监视任意多个主服务器,以及这些主服务器属下的从服务器。

当被监视的主服务器下线时,根据某些规则挑选一个从服务器,作为新的主服务器。接着,其他从服务器会向新的主服务器发送复制指令,并且完成复制。同时,哨兵会监视下线的原主服务器,在它重新上线后,将它也置为从服务器。

二、哨兵启动与初始化

1、启动命令

有两个命令,效果完全相同:

redis-sentinel /path/to/sentinel/config/sentinel.conf
或 redis-server/path/to/sentinel/config/sentinel.conf --sentinel

启动后,会做五件事:初始化服务器、将普通redis使用的代码替换成sentinel专用的代码、初始化sentinel状态、根据给定的配置文件去初始化监视的主服务器列表、创建连向主服务器的网络连接。

2、初始化服务器

由于sentinel服务器是特殊的redis服务器,因此其一开始的初始化服务器和redis普通的服务器初始化大体一致。区别在于sentinel不会载入rdb、aof文件。

3、使用sentinel专用代码

在载入常量、命令表时,会载入sentinel专用的内容。例如sentinel默认的端口是26379,命令表只有七个命令可以执行,包括ping、info、sentinel以及发布订阅相关的四个命令(不含publish命令),而且这七个命令的定义和普通的redis服务器也有所不同。

因此,客户端无法对sentinel服务器请求redis普通服务器的命令,如get、set等。

4、初始化sentinel状态

sentinel状态会初始化sentinelState结构体,这里面保存了sentinel特有的属性,普通的属性仍会初始化在redisServer结构体中。

struct sentinelState{
//当前纪元,用于实现故障转移
uint64_t current_epoch;
//字典形式保存这个sentinel监视的所有主服务器,键是服务器名称,值是指向sentinelRedisInstance指针
dict *masters;
//是否进入TILT模式
int titl;
//目前正在执行的脚本数量
int running_scripts;
//进入TILT模式的时间
mstime_t tilt_start_time;
//最后一次执行时间处理器的时间
mstime_t previous_time;
//FIFO队列,包含所有要执行的脚本
list *scripts_queue;
}sentinel;

上述即sentinelState结构体中的全部属性。

5、初始化sentinelState中的master属性

1)sentinelRedisInstance

master属性是字典形式保存这个sentinel监视的所有主服务器,键是服务器名称,值是指向sentinelRedisInstance指针。

每个sentinelRedisInstance结构(实例结构)是一个被sentinel监视的主服务器、从服务器或其他sentinel。

实例结构属性非常多,主要的属性:name表示服务器名称,从服务器和其他sentinel名称采用“ip:端口”的方式命名,主服务器由sentinel来自动命名;runid表示运行id;addr记录实例的地址;down_after_period记录主观下线时间;quorum表示接收到多少个投票后,服务器算是客观下线。

其中,addr的地址,是一个结构体,包括字符数组形式的ip以及int形式的端口号。

2)初始化

对sentinel的初始化会引起对masters字典的初始化,而masters字典的初始化,由载入的sentinel的配置文件决定。

例如如下配置文件:

则masters字典如下图所示:

其中的每一个sentinelRedisInstance如下图所示:

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

初始化的最后一步,即创建连向主服务器的网络连接。sentinel将成为主服务器的客户端,对它发送命令,并获取有关的回复。

sentinel会和每一个主服务器都创建两个连接,一个是命令连接,专门用于向主服务器发送命令与接收主服务器的回复;另一个是订阅连接,专门用于订阅主服务器的__sentinel__:hello频道。

由于发布订阅时候,信息都不会保存在redis服务器,为了保证保存hello频道的每一条信息,必须专门有一个订阅的连接。另外,除了订阅频道外,sentinel必须要能给主服务器发送命令,以此来与主服务器通信,因此命令连接也是必不可少的。

三、获取主服务器信息

sentinel默认会每10秒一次,给主服务器发送info命令,并且通过分析info命令,来判断当前主服务器的信息。

通过分析info命令,sentinel会得到两方面的信息:一是主服务器的运行ID以及其role域记录的服务器角色,二是关于主服务器属下所有从服务器的信息,由slave字符串开头,每行IP记录一个IP地址、端口号、offset(用于与主服务器aof)、lag(延迟时间)等。

因此,sentinel无序建立和从服务器的连接,也可以知道从服务器的情况。sentinel只需要将info获得的返回结果,分析并更新到sentinel的sentinelState结构体的相应属性即可。

另外,sentinel在更新结构体时,还会分析每一个从服务器是否存在,如果是现有的则更新结构体,如果不是现有的则新增一个结构体。

从上述可知,主从服务器sentinelRedisInstance的主要区别,一是flags属性主是SRI_MASTER而从是SRI_SLAVE;二是名称主服务器是sentinel起的,从服务器是IP:端口号;三是主服务器有个slaves属性,指向一个字典,该字典键是从服务器的IP:端口号,值是表示从服务器的sentinelRedisInstance。

四、获取从服务器信息

当sentinel发现有新的从服务器,除了会为其创建实例结构,还会与其建立连接,连接也是包括订阅连接和命令连接。且默认下,每十秒也会给从服务器发送info命令。

根据info命令,可以提取出以下主要信息:运行ID、角色、主服务器ip与端口、主从连接状态、从服务器优先级、从服务器偏移量。

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

默认情况下,sentinel会每两秒一次,向监听的主从服务器发送以下格式的命令:

publish __sentinel__:hello “<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>”

其中,s开头的是监听的服务器相关的信息,m开头的是sentinel的信息。

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

1、接收信息

上面已经提到,sentinel会和每个监听的服务器建立发布订阅连接,监听__sentinel__:hello频道,会一直监听到连接断开为止。

采用发布订阅的方式,是因为如果不止一个sentinel监听该主从结构的各服务器,则当其中某一个sentinel发送上述第五步的publish的命令,服务器回复在频道信息的,可以被所有监听的sentinel获取到。

sentinel接收到信息将与sentinel的运行id进行比对,如果一致则表示信息是自身发送的,sentinel将丢弃不处理信息;如果不一致表示是其他sentinel发送的命令,则会进行比对并更新相应内容。

2、更新sentinels字典

sentinel为主服务器建立的sentinels字典除了保存自身信息,还会保存其他sentinel的信息,字典的键是sentinel的ip:port,值是对应的sentinel实例。

获取的结果中,如果已经存在名称,则是原来就有是sentinel,否则是新的sentinel则要新开一个空间进行存储。

整体的存储方式和存储主从服务器的方式基本一致。

3、创建连向其他sentinel的连接

当sentinel发现其他sentinel,不仅会记录其结构信息,还会建立一个命令连接,而新的sentinel也会向该sentinel建立命令连接。最终监视主服务器的多个sentinel将形成网络。

另外,sentinel不会和其他sentinel建立订阅连接,因为不需要订阅相关信息。sentinel之间发送命令和订阅信息,只需要互相用命令连接发送即可。

——written by linhxx 2017.09.13