哨兵(sentinel)是Redis高可用的解决方案,用来管理Redis实例,主要是监控、自动故障转移、通知、配置提供者。
1、sentinel的初始化
当一个 Sentinel 启动时, 它需要执行以下步骤:
a) 初始化服务器。sentinel本质上是一个运行在特殊模式下的Redis实例。该模式下redis实例主要使用发布订阅、文件事件处理器、和时间事件处理器的的功能。
b) Redis 服务器使用的代码替换成 Sentinel 专用代码。如服务的端口号,支持不同的命令列表等。
c) 初始化 Sentinel 状态。保存所有和sentinel相关的状态,如配置纪元、监听的主服务器字典等。
d) 初始化 Sentinel 的监视主服务器列表。sentinelRedisInstance结构是被sentinel监听的redis实例结构,可以为主服务器、从服务器或者sentinel。
typedef struct sentinelRedisInstance { // 标识值,记录了实例的类型,以及该实例的当前状态 int flags; // 实例的名字 // 主服务器的名字由用户在配置文件中设置 // 从服务器以及 Sentinel 的名字由 Sentinel 自动设置 // 格式为 ip:port ,例如 "127.0.0.1:26379" char *name; // 实例的运行 ID char *runid; // 配置纪元,用于实现故障转移 uint64_t config_epoch; // 实例的地址 sentinelAddr *addr; // SENTINEL down-after-milliseconds 选项设定的值 // 实例无响应多少毫秒之后才会被判断为主观下线(subjectively down) mstime_t down_after_period; // SENTINEL monitor <master-name> <IP> <port> <quorum> 选项中的 quorum 参数 // 判断这个实例为客观下线(objectively down)所需的支持投票数量 int quorum; // SENTINEL parallel-syncs <master-name> <number> 选项的值 // 在执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量 int parallel_syncs; // SENTINEL failover-timeout <master-name> <ms> 选项的值 // 刷新故障迁移状态的最大时限 mstime_t failover_timeout; // ... } sentinelRedisInstance;
通过读取sentinel配置文件来初始化master字典中的结构。
e) 创建连向主服务器的网络连接。创建与主服务器的命令连接和订阅连接。
2、自动发现从服务器和sentinel实例
sentinel通过INFO命令获取主服务器的运行id等信息以及所有从服务器的信息,对sentinelState结构进行更新。同时创建到从服务器的命令连接和订阅连接,也通过每十秒一次的INFO命令获取从服务器的状态。每个master的sntinelRedisInstance中包含一个slave字典,保存从服务器。示意图如下图
sentinel会向监听的所有主服务器和从服务器的hello频道发送信息,其他监听该主服务器sentinel通过订阅连接可接收到该信息,用于更新对其他sentinel的认知。监听主服务器的sentinelRedisInstance中包含一个sentinel字典保存监听主服务器的sentinel实例。同时会创建一个命令连接,用于之后的故障转移等。
3、监控
sentinel会每秒一次的频率与之前创建了命令连接的实例发送PING,包括主服务器、从服务器和sentinel实例,以此来判断当前实例的状态。down-after-milliseconds时间内PING连接无效,则将该实例视为主观下线。之后该sentinel会向其他监控同一主服务器的sentinel实例询问是否也将该服务器视为主观下线状态,当超过某quorum后将其视为客观下线状态。
当一个主服务器被某sentinel视为客观下线状态后,该sentinel会与其他sentinel协商选出零头sentinel进行故障转移工作。每个发现主服务器进入客观下线的sentinel都可以要求其他sentinel选自己为领头sentinel,选举是先到先得。同时每个sentinel每次选举都会自增配置纪元,每个纪元中只会选择一个领头sentinel。如果所有超过一半的sentinel选举某sentinel领头sentinel。之后该sentinel进行故障转移操作。
如果一个Sentinel为了指定的主服务器故障转移而投票给另一个Sentinel,将会等待一段时间后试图再次故障转移这台主服务器。如果该次失败另一个将尝试,Redis Sentinel保证第一个活性(liveness)属性,如果大多数Sentinel能够对话,如果主服务器下线,最后只会有一个被授权来故障转移。 同时Redis Sentinel也保证安全(safety)属性,每个Sentinel将会使用不同的配置纪元来故障转移同一台主服务器。
4,、故障转移
首先是从主服务器的从服务器中选出一个从服务器作为新的主服务器。选点的依据依次是:网络连接正常->5秒内回复过INFO命令->10*down-after-milliseconds内与主连接过的->从服务器优先级->复制偏移量->运行id较小的。选出之后通过slaveif no ont将该从服务器升为新主服务器。
通过slaveof ip port命令让其他从服务器复制该信主服务器。
最后当旧主重新连接后将其变为新主的从服务器。注意如果客户端与就主服务器分隔在一起,写入的数据在恢复后由于旧主会复制新主的数据会造成数据丢失。
故障转移成功后会通过发布订阅连接广播新的配置信息,其他sentinel收到后依据配置纪元更大来更新主服务器信息。Sentinel保证第二个活性属性:一个可以相互通信的Sentinel集合会统一到一个拥有更高版本号的相同配置上。