Redis(二十五):Sentinel——Redis的哨兵模式原理(一)

Sentinel实现Redis高可用性的一个解决方案

首先我们来理解什么是高可用性

高可用性通常来描述一个系统的平均无故障时间,即服务器正常对外提供服务的时间,通过减少服务器的停工时间

Sentinel(哨岗、哨兵)是Redis的高可用性的解决方案之一。

由一个Sentinel实例组成的Sentinel系统可以监视

整体来说是由一个或者多个Sentinel实例去组成的Sentinel系统,该系统可以监视多个任意多个主从服务器(一旦发现主服务器宕机了,就要在其属下的从服务器进行选举,充当主服务器),当被监视的主服务器进入下线状态,要从其从服务器选举出一台升级为新的主服务器,代替原来的主服务器继续处理命令请求

图示是一个Sentinel系统监视服务器的栗子
在这里插入图片描述

  • Sentinel:就是哨兵,不止一个,因为如果哨兵挂了也是要有替补的

  • master:主服务器

  • Server:从服务器

  • 哨兵要对所有主从服务器进行监视

假设此时如果master下线了,哨兵就会知道该情况发生,然后进行计时,当主服务器的下线时间超过用户设定的下线时长上限时,Sentinel系统就会对原来的主服务器做故障转移的操作

  1. Sentinel系统首先会挑选随便一个server升级成主服务器
  2. Sentinel系统将剩余的从服务器发送指令,然他们成为新的主服务器的从服务器,并进行复制工作(前面提到的从与主要保持一致),当所有的从服务器都开始复制新的主服务器时,故障转移操作就完毕了,等待复制完成就好
  3. Sentinel对断开的原主服务器也会保持一个监视状态,如果原主服务器成功连接上,但超时了,会成为新主服务器的从服务器(也就是降级了),然后对新主服务器进行复制
    在这里插入图片描述
    下面就学习一下,Sentinel系统对主服务器执行故障转移的整个过程

启动并且初始化Sentinel

其实Sentinel就是一个Redis服务器

下面的命令可以启动sentinel服务器(具体怎么搭建,回看前一篇的单机搭建Sentinel)

redis-sentinel /sentinel配置文件
redis-server /sentinel配置文件 --sentinel

当一个sentinel启动时,需要下面的几个步骤

  1. 初始化哨兵服务器
  2. 将普通Redis服务器使用的代码替换出Sentinel专用代码
  3. 初始化sentinel状态
  4. 根据配置文件,初始化Sentinel的监视主服务器列表
  5. 创建连向主服务器的网络连接

初始化Sentinel服务器

Sentinel本质上只是一个运行在特殊模式下的Redis服务器,所以它的初始化步骤和普通的Redis服务器差不多(回看前面的服务器),但要注意的是:Sentinel的初始化过程与普通的Redis服务器并不是完全一致,比如Sentinel不需要恢复数据,所以不需要去进行载入RDB文件或者AOF文件

下面看看Sentinel模式下的Redis可以用哪些命令

功能使用情况
数据库和键值对方面的命令不使用
事务命令不使用
脚本命令不使用
持久化命令,RDB与AOF不使用
主从复制命令Sentinel服务器内部会使用,但连接上Sentinel的客户端不可以使用
发布与订阅命令订阅的4个命令都可以被Sentinel服务器内部和其客户端使用,但发布的命令(PUBLISH)只可以在Sentinel内部使用
文件事件处理器(前面提到过,这个处理器负责接收命令请求和处理命令回复)Sentinel内部使用,但关联的文件事件处理器与普通的Redis不同
时间事件处理器(执行ServerCron函数)Sentinel的内部使用,时间事件的处理器仍然是去执行ServerCron函数,但该模式下会调用sentinelTime函数,这个函数包含了Sentinel要执行的定时操作

使用Sentinel的专用代码

现在已经初始化一台Sentinel服务器了,但里面的一些结构和代码是普通的Redis服务器的,此时要将替换成Sentinel专用的结构和代码

比如前面提到为什么Sentinel有一些命令不能用,就是因为其把redisCommandTable(服务器状态里面的命令表属性)替换成了sentinelcmds(Sentinel专用的命令)。

这也是为什么在Sentinel模式下,并不能执行一些键值对操作命令,因为压根没有载入这些命令,载入的命令只有下面的7个

  • info(查看服务器状态)
  • ping(访问)
  • sentinel(哨兵的命令,其实这个对应的是哨兵配置文件里面的参数,客户端可以进行修改)
  • subscribe(订阅)
  • unsubscribe(退订)
  • psubscribe(订阅指定格式的频道)
  • punsubscribe(退订指定格式的频道)

初始化Sentinel状态

初始化Sentinel服务器后,有了自己的RedisServer状态,然后将一些原来的代码替换成Sentinel专用的代码后,接下来就是要初始化Sentinel状态(实际只是创建这个实例),这个状态其实是SentinelState结构

注意:服务器状态还是保存在RedisServer结构中,而Sentinel状态则保存在sentinelState结构中

结构里面的属性如下所示

struct sentinelState(
	unit64_t current_epoch; //记录当前纪元,用于实现故障转移的
    //保存了监视的所有主服务器的字典
    //字典的键就是主服务器的名字
    //而字典的值是一个指向sentinelRedisInstance(实例)结构的指针
    dict *master;
    //是否进入了TILT模式
    int tilt;
    //目前正在执行脚本的数量
    int running_scripts;
    //进入TILT模式的时间
    mstime_t previous_time;
    //最后一次执行时间处理器的时间
    mstime_t previous_time;
    //一个FIFO队列,包含了所有需要执行的用户脚本
    list *scripts_queue;
)sentinel;

初始化Sentinel状态的masters属性

初始化Sentinel状态后,就开始给里面的属性赋值了,下面就是载入配置文件,然后去初始化master属性

Sentinel状态中的masters字典记录了所有被Sentinel监视的主服务器的相关信息

  • 字典的键是被监视主服务器的名字(就是配置选项里面的mymaster)
  • 而字典的值则是被监视的主服务器对应的sentinelRedisInstance结构

每一个SentinelRedisInstance结构都代表着一个被Sentinel监视的一个Redis服务器实例,不一定是主服务器,也可以是从服务器,甚至是另一个Sentinel,SentinelRedisInstance里面的属性很多,下面只介绍SentinelRedisInstance为主服务器时候的属性

typedef struct sentinelRedisInstance(
	//标识值,记录了实例的类型,以及实例的当前状态
    int flags;
    
    //实例的名字(在配置文件那里载入)
    //如果是从服务器或者另一个sentinel,名字是由sentinel自己设置的
    //格式为:ip:port,
    char *name;
    
    //实例的运行ID
    char *runid;
    
    //配置纪元,也是用来实现故障转移的
    unit64_t config_epoch;
    
    //实例的地址
    sentinelAddr *addr;
    
    //实例无响应杜少毫秒之后被判断为主观下线(主观下线是指,哨兵自己认定监视的主服务器挂了)
    //对应日志里面的就是+sdown(subjectively down)
    //对应的是配置文件里面的sentinel down-after-millseconds参数
    mstime_t down_after_period;
    
    //判断这个实例为客观下线所需要的投票数量
    //对应的就是配置文件里面的sentinel monitor <master-name> <ip> <port> <quorum>
    //的quorum参数,也就是指至少多少个哨兵认为这台服务器是主观下线,就会变成客观下线
    int paraller_syncs;
    
    //刷新故障迁移状态的最大时限
    //对应配置未见里面的sentinel failover-timeout <master-name> <ms>选项的值
    //也就是后面ms的值
    //改属性是指:主服务器被认为客观下线,会被踢掉,然从服务器替代成为新的主服务器
    //然后其余的从服务器要对新的主服务器进行复制(主从复制)
    //但这个复制时间是可以设置限制的,超过这个时间限制就判定无效,需要重来
    mstime_t failover_timeout;
    //....
)

可以看到下面的slave名字为:ip地址:端口号

在这里插入图片描述

addr属性

从上面,可以看到这个属性是指向一个sentinelAddr结构的指针,这个结构保存着实例的IP地址和端口号

typedef struct sentinelAddr(
	char *ip;
    int port;
)sentinelAddr;

初始化后的SentinelState结构大概如下所示在这里插入图片描述

创建连接上主服务器的网络连接

经过上面的步骤,已经完善了SentinelState信息,下面可以进行连接了

此时的Sentinel会成为主服务器的客户端(如果主服务器有密码的话,配置文件那里也要去设置),可以向主服务器发送命令,并从命令回复中获取相关的信息

Sentinel会创建两个连向主服务器的异步网络连接,一个是命令连接,一个是订阅连接

  • 命令连接:该连接专门用于向主服务器发送命令,然后接收主服务器的命令回复
  • 订阅连接:这个连接专门用于订阅主服务器的**_ sentinel _ :hello**频道

为什么要用两个连接

Redis目前的发布与订阅功能中,被发送的信息是不会被保存到Redis数据库里面的(即接收信息的用户并不会保留频道发布的信息),如果在信息发送时,假如接收信息的客户端不在线或者断线,这个客户端就会丢失这条信息了,甚至断线了的话,前面的信息也会消失掉,所以Sentinel用了一个专门的连接去进行监听频道信息,这是为了不丢失任何sentinel_:hello频道里的任何信息(不太理解这句话)

另一方面,除了订阅频道外,Sentinel还必须向主服务器发送命令,以此来与主服务器进行通信,所以Sentinel还必须向主服务器创建命令连接

因为一个Sentinel需要与多个实例创建多个网络连接,所以Sentinel使用的是异步连接

异步连接是指一个服务器可以同时去响应多个请求,而同步连接是指一个服务器与第一个请求建立连接后,并且开始进行通信,此时第二个请求之后都会被阻塞
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值