Sentinel(哨兵、哨岗)是Redis的高可用(high availability)解决方案:由一个或多个Sentinel实例组成Sentinel系统可以监视任意多个主服务器以及它们属下的所有从服务器,并在监视主服务器进行下线时,将主服务器下属的从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

从Redis的Sentinel主从服务关系中可以看出:
Sentinel本质上只是一个运行在特殊模式下的Redis服务器,所以启动Sentinel的第一步就是初始化一个普通的Redis服务器,并将普通Redis服务器使用的代码替换成Sentinel专用代码,然后再进行初始化,具体如下:
struct sentinelState { // sentinel id char myid[CONFIG_RUN_ID_SIZE+1]; // 当前纪元,用于实现故障转移 uint64_t current_epoch; // 保存了所有被这个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需要监听的所有master实例,这些数据都是通过sentinel.conf配置文件获取的
typedef struct sentinelRedisInstance { // 标识值,记录了实例的类型以及该实例的当前状态 int flags; // 实例的名字 // 主服务器的名字由用户在配置文件中的设置 // 从服务器以及Sentinel的名字由Sentinel自动设置 // 格式为ip:port,例如"127.0.0.1:26397" char *name; // 实例运行的ID char *runid; // 配置纪元,用于实现故障转移 uint64_t config_epoch; // 实例的地址 sentinelAddr *addr; // 实例无响应多少毫米之后才会被判断为主观下线 mstime_t down_after_period; // 监听同一个master节点的其它sentinel dict *sentinels; // master的所有slave节点 dict *slaves; // 判断这个实例客观下线所需的支持投票数量 unsigned int quorum; // ...... // master实例 struct sentinelRedisInstance *master; // 如果是master实例,表示执行故障转移的sentinel的runid // 如果是sentinel,表示被投票成leader的sentinel的runid char *leader; // 刷新故障迁移状态的最大时限 mstime_t failover_timeout; // ...... } sentinelRedisInstance;sentinelState.masters中的实例为sentinelRedisInstance的master对象。sentinelRedisInstance.slaves表示该master对象对应的所有slave,sentinelRedisInstance.sentinels表示该master对象所有被监听的sentinel
3) 创建连向主服务器的网络连接
初始化Sentinel时需要创建连向被监视器主服务器的网络连接,Sentinel将成为主服务器的客户端,它可以向主服务器发送命令,并从命令回复中获取相关的信息。每个Sentinel会创建两个连向主服务器的异步网络连接:
__sentinel__:hello频道Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的当前信息。Sentinel可以获取以下两个方面的信息:
runid和服务器的角色ip、port等信息。同时将从服务器的信息更新到sentinelRedisInstance.slaves属性中当Sentinel发现主服务器有新的从服务器出现时,Sentinel除了会为这个新的从服务器创建相应的实例结构之外,Sentinel还会创建连接到从服务器的命令连接和订阅连接。
Sentinel会对__sentinel__:hello频道的订阅一直持续到Sentinel与服务器的连接断开为止。当一个Sentinel收到从__sentinel__:hello频道的一条信息,会对信息进行分析并,然后如下操作:
默认情况下,Sentinel以每秒一次的频率向所有其它创建了命令连接的实例(包括主服务器、从服务器、其它Sentinel在内)发送PING命令,并通过PING命令回复来判断实例是否在线
如果一个实例在down-after-milliseconds毫米内(Sentinel配置文件中配置项),连续向Sentinel返回无效回复,那么Sentinel会在这个实例中flags属性中打开SRI_S_DOWN标识,以此来表示这个实例已经进入主观下线状态
当Sentinel将一个主服务器判定为主观下线之后,为了确保这个主服务器是否真的下线,它会询问其它同样监视这一主服务器的其它Sentinel,具体步骤如下:
is-master-down-by-addr命令is-master-down-by-addr命令,检测主服务器是否已下线is-master-down-by-addr命令的回复,通过判断同意主服务器下线Sentinel的数量是否大于quorum来判定是否主服务器被标记为客观下线当一个主服务器被判定为客观下线时,监视这个主服务器的所有Sentinel会进行协商,选出一个leader执行故障转移,选举规则如下:
current_epoch的值都会自增一次is-master-down-by-addr命令中的runid参数的值(*除外)来表示leader的runid选举产生最为leader的Sentinel将执行对主服务器的故障转移操作,主要包含三个步骤: