学习小记 -- Redis的哨兵(Sentinel)你了解吗?

目录

启动并初始化Sentinel

 获取主服务信息

获取从服务器信息

向主从服务器建立订阅连接

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

更新Sentinels字典

主观下线与客观下线

主观下线

客观下线

选举

选举领头Sentinel

故障转移

选举新的主服务器

总结


Sentinel是Redis的高可用解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视人意多个主服务器,以及这些主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器代替已下线的主服务器继续处理命令请求。

启动并初始化Sentinel

Sentinel本质上就是一个运行在特殊模式下的Redis服务器,所以:

启动Sentinel的第一步:就是初始化一个普通的Redis服务器,因为功能不同,所有他俩的初始化的过程并不相同。普通服务器会在初始化过程中载入RDB文件或者AOF文件,因为Sentinel不适用数据库,所以Sentinel初始化是不会载入RDB文件或者AOF文件。

第二步就是使用Sentinel的专用代码替换Redis的代码,从而实现端口,命令表等的替换。PING,SENTINE,INFO,SUBSCRIBE,UNSUBSCRIBE,PSUBSCRIBE和PUNSUBSCRIBE这7个命令是客户端对Sentinel的所有命令。

第三步:初始化Sentinel状态。服务器会初始化一个sentinelState的结构,这个结构保存了所有和Sentinel有关的状态。

struct sentinelState {

    // 当前纪元
    uint64_t current_epoch;     /* Current epoch. */

    // 保存了所有被这个 sentinel 监视的主服务器
    // 字典的键是主服务器的名字
    // 字典的值则是一个指向 sentinelRedisInstance 结构的指针
    dict *masters;      /* Dictionary of master sentinelRedisInstances.
                           Key is the instance name, value is the
                           sentinelRedisInstance structure pointer. */

    // 是否进入了 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. */

    // 一个 FIFO 队列,包含了所有需要执行的用户脚本
    list *scripts_queue;    /* Queue of user scripts to execute. */

} sentinel;

第四步:初始化Sentinel状态的masters属性:这个属性中记录了说有被Sentinel监视的主服务器的相关信息。masters是一个字典,字典的键是被监视主服务器的名字,值是一个实例结构sentinelRedisInstance。(此过程是根据被载入的Sentinel配置文件来进行的)

typedef struct sentinelRedisInstance {
    
    // 标识值,记录了实例的类型,以及该实例的当前状态
    int flags;      /* See SRI_... defines */
    
    // 实例的名字
    // 主服务器的名字由用户在配置文件中设置
    // 从服务器以及 Sentinel 的名字由 Sentinel 自动设置
    // 格式为 ip:port ,例如 "127.0.0.1:26379"
    char *name;     /* Master name from the point of view of this sentinel. */

    // 实例的运行 ID
    char *runid;    /* run ID of this instance. */

    // 配置纪元,用于实现故障转移
    uint64_t config_epoch;  /* Configuration epoch. */

    // 实例的地址
    sentinelAddr *addr; /* Master host. */

    ...
    ...
    ...
} sentinelRedisInstance;

sentinelState以及sentinelRedisInstance的结构图如下: 

 第五步:创建连向监视主服务器的网络连接,Sentinel会向主服务器发送两个异步连接:

  1.         命令连接:专用发送命令,接收回复。
  2.         订阅连接:专门用于订阅主服务器的_sentinel_:hello频道。

 获取主服务信息

Sentinel默认会每十秒向被监视的主服务器发送INFO命令,得到主服务信息的回复。包括服务器运行ID,角色等,拿到这部分用于更新主服务器的实例结构。还有从回复中得到主服务下属的从服务器的信息,包括IP地址和端口号等,用这部分信息更新主服务器实例结构的slaves字典,这个字段记录了主服务器下从服务器的名单。

所以,到这里,画个整体的结构图一下子就看懂了:

获取从服务器信息

Sentinel默认每十秒一次向从服务器发送INDO命令,获得回复有:从服务的运行ID,角色role,主服务器的IP地址及端口号,主从服务器的连接状态,从服务器的优先级,从服务器的复制偏移量等。

向主从服务器建立订阅连接

Sentinel会每2秒一次向向所有被监视的主从服务器发送PUBLISH:_sentinel_:hello命令:其中包括Sentinel本身的信息和主从服务器的信息(IP地址,端口号,运行ID,配置纪元等)。由此建立订阅连接。

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

Sentinel通过订阅连接从频道中接收服务器信息。

对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息也会被其他Sentinel接收到,用于更新其他Sentinel对发送信息Sentinel的认知以及被监视服务器的认知。

举例:Sentinel1,Sentinel2,Sentinel3都监视同一个服务器,当Sentinel1发送一条信息时,Sentinel1、Sentinel2和Sentinel3都可以接收到这条信息,根据信息里的运行ID,IP地址和端口号判断,如果是自己发的则不做处理,如果不是,则对相应的主服务的实力结构进行更新。

更新Sentinels字典

Sentinel为主服务创建的实例结构中的Sentinels字典保存了除Sentinel本身之外,还有同样监视这个主服务器的其他Sentinel的资料,当一个Sentinel(目标Sentinel)接收到其他Sentinel(源Sentinel)发来的信息时,目标Sentinel会从信息中提取出与Sentinel和主服务器有关的信息,IP地址,端口号,运行ID等。

目标Sentinel会在自己的Sentinel状态的masters字典中找相应的主服务器实例结构,然后根据提取出的Sentinel参数,检查主服务器实例结构的Sentinel字典中,源Sentinel的实例结构是否存在,不存在添加,存在更新。

至此,整个Sentinel的结构图如下:

当Sentinel通过频道信息发现一个新的 Sentinel时,它不仅会为新Sentinel在sentinels字典中创建一个新的实例结构,还会创建一个连向新Sentinel的命令连接。最终监视同一主服务器的多个Sentinel将形成互相连接的网络::

注:Sentinel在连接主从服务器时,会同时创建命令连接和订阅连接,但是在连接其他Sentinel时,却只会创建命令连接。因为Sentinel需要通过接收主从服务器发来的频道信息来发现未知的新Sentinel,所以需要建立订阅连接,而相互已知的 Sentinel只要使用命令连接来进行通信就够了。

主观下线与客观下线

主观下线

默认情况下,sentinel会每秒一次向所有与他创建了命令连接的实例发送PING命令,通过返回的信息判断实例是否出于在线状态。配置文件中的down-after-millisenconds参数定义了超时时间。当sentinel判定实例超时时,会将这个实例所对应实例结构中分flag属性设为“SRI_DOWN”。

配置文件中的down-after-millisenconds参数不仅会被Sentinel用来判断主服务器的主观下线状态,还会被用于判断主服务器属下所有从服务器,已经所有同样监视这个主服务器的其他Sentinel的主观下线状态。

客观下线

当Sentinel将一个主服务器判断为主观下线时,会向其他同样监视这个主服务器的Sentinel发送SENINEL命令,会收到是否这些Sentinel也同意将主服务器下线的回复,统计这些结果,如果这一数量达到配置指定的客观下线所需的数量时,Sentinel会将主服务器实例结构的flag属性改为SRI_O_DOWN,表示主服务器已经进入客观下线状态。

选举

选举领头Sentinel

当一个主服务器客观下线后,监视这个主服务器的各个Sentinel会进行协商,选出一个零头Sentinel,由这个领头Sentinel对下线的主服务器进行故障转移。

选举规则大致如下:

  1. 每个在线的Sentinel都有资格。
  2. 在一个配置纪元里面只会出现一个领土Sentinel。
  3. Sentinel设置局部领头Sentinel遵循先到先得的规则:最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,之后再向目标Sentinel发送的请求会被拒绝。
  4. 目标回复的内容包含局部领头Sentinel的运行ID和配置纪元。
  5. 源Sentinel收到目标回复后对比是否跟自己的配置纪元相同,若相同且运行ID跟自己也一致,则证明自己被目标Sentinel选中。
  6. 若某个Sentinel被半数以上的Sentinel选中成为局部领头Sentinel,那么他将被选为领头Sentinel。
  7. 选举结束后,所有配置纪元+1。

故障转移

  1. 在已下线的主服务器下属的所有的从服务器中选择一个作为主服务器。
  2. 将这些从服务器改为复制新的主服务器。
  3. 将已下线的主服务器设置为新的主服务器的从服务器,当这个服务器重新启动时,就会变为新主服务器的从服务器。

选举新的主服务器

  1. 领头Sentinel将已经下线主服务器的所有从服务器保存到一个列表里。
  2. 删除处于下线或者断线状态的服务器。
  3. 删除5秒内没有回复领头SentinelINFO命令的服务器。
  4. 删除所有与下线主服务器连接断开超过down-after-millsenconds*10毫秒的服务器。
  5. 领头Sentinel根据服务器的优先级对从服务器排序,选取优先级最高的。
  6. 若优先级相同,选取复制偏移量最大的(偏移量最大保存着最新数据)。
  7. 若偏移量相同,选取运行ID最小的服务器。

总结

  1. Sentinel只是一个运行在特殊模式下的Redis服务器,命令表不同。
  2. Sentinel读取配置文件,为每个被监视的主服务器创建实例结构,并创建命令连接和订阅连接。
  3. Sentinel向主服务器发送INFO命令来获取主服务器及下属所有从服务器的地址信息,并为这些从服务器创建实例结构,以及他们建立命令和订阅连接。
  4. 当Sentinel正在对主服务器进行故障转移操作时,Sentinel向从服务器发送INFO命令从原来的10秒一次改为1秒一次。
  5. 监视同一主服务的多个Sentinel服务,会每2秒发送一个hello消息宣告自己存在。
  6. Sentinel只会与主服务器和从服务器创建命令连接和订阅连接,Sentinel与Sentinel之间只会创建命令连接。
  7. Sentinel每秒1次向实例发送PING命令,判断实例是否在线。
  8. Sentinel收到超过半数的同意下线回复后,会将主服务器判断为客观下线,并发一次故障转移。

 参考:《Redis设计与实现》--黄健宏

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值