Redis 哨兵 sentinel - litter-fish/ReadSource GitHub Wiki

哨兵

Redis高可用的解决方案:由一个或多个Sentinel实例组成哨兵系统,通过监视一个或多个主服务器,及下面的所有从服务器,并在监视的主服务器下线时,可以自启动将主服务器下面的某台从服务器自动升级为新的主服务器

启动并初始化哨兵

  1. 初始化服务器
  2. 替换哨兵专用代码,替换端口、命令表等
void initSentinelConfig(void) {
    server.port = REDIS_SENTINEL_PORT;
}

哨兵命令表

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},
    {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0}
};
  1. 初始化哨兵状态
struct sentinelState {
    // 当前纪元,用于故障转移
    uint64_t current_epoch;     /* Current epoch. */
    // 保存所有被哨兵监控的主服务器
    // key是一个主服务器的名字
    // value是一个sentinelRedisInstance结构指针
    dict *masters;
    // 是否进入TILT模式
    int tilt;           /* Are we in TILT mode? */
    // 目前正在执行脚步的数量
    int running_scripts;    /* Number of scripts in execution right now. */
    // 进入TILT的时间
    mstime_t tilt_start_time;   /* When TITL started. */
    mstime_t previous_time;     /* Last time we ran the time handler. */
    list *scripts_queue;    /* Queue of user scripts to execute. */
    char *announce_ip;      /* IP addr that is gossiped to other sentinels if
                               not NULL. */
    int announce_port;      /* Port that is gossiped to other sentinels if
                               non zero. */
} sentinel;
  1. 初始化Sentinel的masters
  2. 创建连向主服务器的网络连接,创建连向被监视主服务器的网络连接,sentinel将成为主服务器的客户端,它可以向主服务器发送命令,并等待主服务器的回复。 a2c70a973527962652c3aa4d91c7b32d.png

为何需要创建两个连接???? 一个是命令连接,用于向主服务器发送命令,并接受命令回复 另一个是订阅连接,用于订阅主服务器的_Sentienl_::hello频道

订阅连接

  1. 获取主服务器信息 Sentinel默认10秒一次的频率向主服务器发送Info命令,并通过分析INFO命令的回复获取当前信息。 4b7fd768f04bcb8363e17141d922cc16.png

  2. 获取从服务器信息 Sentinel默认10秒一次的频率向从服务器发送Info命令 4b1a5e2b8277430099426ebcef48fdeb.png

从服务器的实例 c4283e1e67296f6d6f6d49459296b74b.png

  1. 向主服务器和从服务器发送信息 sentinel默认会按照2秒一次的频率向主服务器和从服务器发送命令: ab303b2dcc5a6c8fda55e1c92b0df6e2.png

命令示例: 1e2e3195193ce4274638892bf97fb80d.png

  1. 接收来自主服务器和从服务器的频道信息 对于每个与Sentinel连接的服务器,Sentinel即通过命令连接向服务器的__sentinel__:hello频道发送消息, 有通过订阅连接从服务器的__sentinel__:hello频道接收消息。 d9e4d860f2dd2bdef98c18692beed4f5.png

4.1 更新sentinels字典信息 从订阅信息中分别提取出主服务器和Sentinel信息,并维护到sentinels字典中 c51b81318632def9c847cfd505bdd216.png

4.2 创建连向其他Sentinel的命令连接 通过这个命令连接向其他sentinel进行信息交换 596561322426fee2dc28b60d13db3e38.png

  1. 检测主观下线状态 默认情况下,sentinel会以每秒一次的频率向与它创建命令连接的服务器发送ping命令,通过返回信息判断状态。 ping命令的回复: 有效回复:+PONG、-LOADING、-MASTERDOWN 无效回复:除上面三种或在指定时限内没有返回任何回复。

如果一个实例在down-after-milliseconds毫秒内,连续向sentinel返回无效回复,那么sentinel就会修改这个实例对应的实例结构,打开flag的SRI_S_DOWN标识。 820e19969f422211437c872c907393bf.png

  1. 检测客观下线 当sentinel将一个主服务器判断为主观下线后,为了确认这个服务器是否真的下线,会向同样监控这台主服务器的sentinel进行询问, 只有当超过一定的sentinel认为已下线才会判定为客观下线 a. 发送SENTINEL is-master-down-by-addr <current_epoch> * 命令,询问其他Sentinel是否同意主服务器下线 b. 其他Sentinel服务器接收SENTINEL is-master-down-by-addr命令,并根据IP和端口查询主服务器是否已经下线,然后按照一定格式返回 c. 接收其他Sentinel的命令回复,统计其他Sentinel将主服务器下线的数量,并按照配置判定客观下线,修改实例属性,打开SRI_O_DOWN属性。 bfd5a4dc2ab9bdfecb42472774a084fe.png

  2. 选举领头Sentinel 当主服务器被判定为客观下线,需要选举一个领头Sentinel,负责对下线的主服务器进行故障转移。 通过向其他Sentinel发送 SENTINEL is-master-down-by-addr <current_epoch> 命令, 告诉其他Sentinel将选举作为选举领头Sentinel

故障转移

  1. 选举一个主服务器,在下线的从服务器中,挑选一个状态良好、数据完整的服务器,然后向这个从服务器发送SLAVEOF no one 命令 当向从服务器发送了SLAVEOF 命令后,领头Sentinel会以每秒一次的频率向从服务器发送INFO命令,并观察role角色是否已经变成master 主服务器选择规则: 1)删除所有已经下线或者断线的从服务器 2)删除所有最近5秒没有回复过领头Sentinel的INFO命令的从服务器 3)删除所有与已下线主服务器连接断开超过down-after-milliseconds * 10 毫秒的从服务器 65831792a5f4d1ff7dbadf2b77685b50.png

  2. 处理旧主服务器的从服务器节点:修改从服务器的复制目标,当主服务器被选择后,领头Sentinel将向其他从服务器发送SLAVEOF <master_ip> <master_port> 1fc25943bce812aea5ed4d8c4ebd485f.png

  3. 将旧的主服务器设置为新主服务器的从服务器 f9ab213149366d74cf97785b20b3cb46.png

c3f854d1429ca4aaeb5e6e13e51b4441.png

⚠️ **GitHub.com Fallback** ⚠️