深入理解Redis、被监控得主机-哨兵

什么是哨兵

在此之前,在回顾一下主从复制,主从复制主要有两个作用,一是可以为主服务器提供一个备份,当主服务器发生故障后,在这个备份中会有一份完整得数据,二是对主服务器进行分流,比如实现读写分离,将写操作放在主节点上,将读操作放到从节点上,但是这种模式有个问题,就是如果主节点发送故障,那么故障转移就需要手工来完成,比如选出一个新的slave作为新的master节点。为了解决主从复制故障转移得问题,Redis就引入了高可用哨兵模式。

哨兵模式是由一个或多个哨兵组成得哨兵系统,主要用于监控任意多台主服务器是否发生故障,以及监控这些主服务器得从服务器,当主服务器发生故障时,他会通过投票选举得方式从主服务器下属得服务器中选出一台新得主服务器,让这台新得服务器代替之前得主服务器继续处理命令请求,从而实现了故障得自动转移,无需手工操作,达到了高可用、热部署得目的。

另外哨兵模式是不会存储数据,他的作用是对Redis主从节点进行监控,对其故障进行判断,转移等处理。

哨兵模式配置

假设现在有一台主服务器,两台从服务器已经开启,接着来开启哨兵。
首先打开sentinel.conf配置文件,

port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

sentinel monitor mymaster 127.0.0.1 6379 2:监控一台名为mymaster的主服务器,他的ip地址是 127.0.0.1 ,端口是6379,。最后的2代表当这台主服务器发生故障时,至少需要两台sentinel同意,才能认定主服务器失效。

sentinel down-after-milliseconds mymaster 30000:指定sentinel认为服务器已经离线所需要的毫秒数,默认为30秒,如果服务器在指定时间内没有向sentinel返回PING命令的回复,或者回复一条错误的信息,则sentinel认为这台服务器已经离线,将其标记为主观下线,但是只有多个sentinel将他标记为主观下线后,这台服务器才会被标记为客观下线,才会进行故障转移机制。

sentinel parallel-syncs mymaster 1:指定了在执行故障转移时,最多可以有多少台从服务器同时对新的主服务器进行复制,默认为1,这个值越小,在进行故障转移时所需要的时间就越长。

sentinel failover-timeout mymaster 180000该选项指定在规定时间内如果没有完成failover操作,则认为failover操作失败,默认为180s。

下面是启动sentinel。

./sentinel1/src/redis-sentinel ./sentinel1/sentinel.conf 

连接命令

./redis-6.0.4/src/redis-cli -p 26379

可以使用INFO sentinel进行信息查看。

127.0.0.1:26379> INFO sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=1
127.0.0.1:26379> 

另外Sentinel启动后,配置文件会发生一些变化,会额外多一些信息,如下。

port 26379
daemonize no
pidfile "/var/run/redis-sentinel.pid"
logfile ""
dir "/tmp"
sentinel myid bb7b059d0af8287215df5adc9fdda7ed89f77e85
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0

# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* +@all
sentinel known-replica mymaster 127.0.0.1 6381
sentinel known-replica mymaster 127.0.0.1 6380
sentinel current-epoch 0

好了,我们在同样的方式在启动两个Sentinel,记得修改下配置文件中的端口和logfile路径、

./sentinel-26380/src/redis-sentinel ./sentinel-26380/sentinel.conf 
./sentinel-26381/src/redis-sentinel ./sentinel-26381/sentinel.conf 

至此,哨兵模式就配置完了。

在哨兵模式下部分命令无法使用,如SET、SELECT、DEL等,事务相关的命令也不能使用,还有脚本、RDB、AOF等。原因是哨兵并不使用数据库,不存储任何数据,他使用sentinel.c/sentinelcmds作为服务器的命令表,sentinelcmds函数如下。

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},
    {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0},
    {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0},
    {"role",sentinelRoleCommand,1,"ok-loading",0,NULL,0,0,0,0,0},
    {"client",clientCommand,-2,"read-only no-script",0,NULL,0,0,0,0,0},
    {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0},
    {"auth",authCommand,2,"no-auth no-script ok-loading ok-stale fast",0,NULL,0,0,0,0,0},
    {"hello",helloCommand,-2,"no-auth no-script fast",0,NULL,0,0,0,0,0}
};

这里列举哨兵模式常用的几个命令。

  1. SENTINEL masters: 该命令用于查看哨兵监控的主服务器的相关信息及状态。

  2. SENTINEL slaves < master name >:该命令用于列出指定主服务器的所有从服务器的信息及状态。

  3. SENTINEL get-master-addr-by name < master name >:该命令用于返回指定名字的主服务器的IP地址和端口信息。如果这台主服务器正在执行故障转移操作,或者针对这台主服务器的故障转移操作已经完成,那么这个命令将会返回新的主服务器的IP地址和端口信息。

  4. SENTINEL reset < pattern >:该命令用于重置所有名字和给定模式(pattem) 相匹配的主服务器。pattern 参数是一种Glob风格的模式。一旦执行此命令,它将会清除主服务器目前的所有状态,包括正在执行中的故障转移,以及删除目前已经发现和关联的、主服务器的所有从服务器和Sentinel。请谨慎执行。

  5. SENTINEL failover < master name >:该命令用于当主服务器出现故障时,在不询问其他Sentinel意见的情况下,强制开始一次自动故障转移操作。它会给另外的Sentinel发送一个最新的配置,这些Sentinel在接收到最新的配置之后会自动更新。

  6. NFO:该命令用于查看Redis的相关配置信息。

  7. INFO sentinel:该命令用于查看Sentinel 的基本状态信息。

哨兵原理

  1. 启动Sentinel并初始化
    启动命令:
redis-sentinel sentinel .conf配置文件所在路径

前面说过,Sentinel 并不使用数据库,它不能存储数据,他也不会加载RDB文件或AOF文件。在启动之后,服务器会初始化一个sentinel.c/sentinelState结构,这个结构中保存了服务器中所有和Sentinel功能有关的状态。

struct sentinelState {
    char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */
    uint64_t current_epoch;         /* Current epoch. */
    dict *masters;      /* Dictionary of master sentinelRedisInstances.
                           Key is the instance name, value is the
                           sentinelRedisInstance structure pointer. */
    int tilt;           /* Are we in TILT mode? */
    int running_scripts;    /* Number of scripts in execution right now. */
    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. */
    unsigned long simfailure_flags; /* Failures simulation. */
    int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script
                                  paths at runtime? */
} sentinel;

Sentinel状态中的masters字典记录了所有被Sentinel监控的主服务器信息,字典中的键就是被监控的主服务名字,字典中的值是一个指向sentinel.c/sentinelRedisInstance结构的指针。

对Sentinel状态的初始化也就是对masers字典的初始化,而被加载的sentinel.conf配置文件就用于masters 字典的初始化。

在初始化Sentinel之后,就会建立与被监控主服务器的网络连接,Sentinel 将成为主服务器的客户端,这个客户端可以向主服务器发送命令,并从中获取相关的服务器信息。Sentinel在监控主服务器后,它们之间会建立两个异步网络连接。

一个是命令连接, 这个连接专用于向主服务器发送命令,并获取返回的信息, 以此来与主服务器一直保持通信状态。

另一个是消息订阅连接,这个连接专用于订阅主服务器的_sentinel_hello频道消息。在目前Redis的消息订阅发布功能中,被发送的消息并不会被保存到Redis服务器中。在发送消息的过程中,如果订阅消息的客户端离线或者因为其他原因而接收不到消息。那么这条消息将会丢失。为了保证订阅的_sentinel_hello频道的消息在传输过程中不丢失,Sentinel专门建立了一个订阅该频道消息的网络连接。

  1. 获取主从服务器的信息

在Sentinel 启动并完成初始化后,就开始获取主从服务器的信息。在默认情况下Sentinel会以每10s一次的频率,通过命令连接向被监控的主服务器发送INFO命令,并通过分析INFO命令的回复信息来获取主服务器的当前信息。

Sentinel通过获取INFO命令返回的信息,可以知道被监控主服务器的信息,包括run_id(它记录了服务器的运行ID)和role (它记录了服务器的角色信息)及其他信息。有了主服务器的run id 和role信息,Sentinel 就可以对主服务器的实例进行更新。还可以知道主服务器从属的从服务器的信息,每台从服务器都有一一个以“slave” 字符串开头的行记录。每行的ip选项记录了从服务器的IP地址,port 选项记录了从服务器的端口号。Sentinel取到这些IP地址和端口号之后,无须用户提供从服务器的地址信息,就可以发现从服务器。通过主服务器获取从服务器的信息,获取到的从服务器信息将会用于更新主服务器实例结构的slaves 字典,这个字典用于记录从服务器的信息,字典中的键是从服务器的名字,它由Sentinel 自动设置,格式为ip:port,比如,127.0.0.1:6380, 字典中的值是从服务器对应的实例结构。

  1. 向主从服务器发送信息

Sentinel也会向主从服务器发送信息,它以每2s一次的频率,通过命令连接向所有被监控的主从服务器发送以下命令:

PUBLISH sentinel_:hello "<s_ ip>,<s_ port>,<s_runid>,<s_epoch>, <m_name>,<m ip>,<m_port>, <m_epoch>"

该命令向服务器的_sentinel_:hello 频道发送了条消息,该条消息含有多个参数, 内容如下。

s_ip: Sentinel的IP地址。

s_port: Sentinel的端口号。

s_runid: Sentinel的运行ID.

s_epoch:Sentinel当前的配置纪元(一个配置纪元就是一台新主服务 器配置的版本号)。

m_name:主服务器的名字。

m_ip:主服务器的IP地址。

m_port:主服务器端口

m_epoch:主服务器当前的配置纪元

其中,以s开头的是Sentinel本身的信息,以m开头的是Sentinel所监控的主服务器的信息。

一个Sentinel可以与其他Sentinel进行网络连接,各个Sentinel之间可以互相检查对方的可用性,并进行信息交换。同时,每个Sentinel都订阅了被它监控的所有主从服务器的_sentinel_ :hello 频道,用于判断查找新的Sentinel。 当一个Sentinel 发现个新的 Sentinel时,它会将这个新的Sentinel添加到一个列表中, 而这个列表中保存了Sentinel 己知的监在同一台主服务器的所有其他Sentinel信息。Sentinel 所发送的信息中包含了主服务器的完整信息。如果一个Sentinel包含的主服务器的信息比另一个Sentinel发送的配置要旧,那么这个Sentinel会立即升级到新配置上。

  1. 接收来自主从服务器的频道消息

在Sentinel与主从服务器之间建立消息订阅连接后,Sentiel就会通过消息订阅连接向服务器发送SUBSCRIBE _sentine_:hello命令。Sentinel会一直订阅服务器的_sentine_:hello消息频道,直到它与服务器断开连接为止。当Sentinel 和服务器之间建立连接之后,它就会通过命令连接向服务器的_sentine_:hello频道发送消息,同时通过消息订阅连接来读取服务器的_sentine_:hello频道消息。

当一个Sentinel 读取到_sentine_:hello频道中的信息时,就会从读取的信息中获取Sentinel的IP地址、端口号、运行ID等,用于做以下判断:

  • 如果信息记录的Sentinel的运行ID和接收信息的Sentinel 的运行ID相同,就说明这条信息是由它自己发送的,将移除这条信息,不做任何处理。

  • 如果信息记录的Sentinel的运行ID和接收信息的Sentinel的运行ID不相同,就说明这条信息是由其他Sentinel发送的,接收信息的Sentinel将会根据信息中的各个参数,对相应的主服务器的实例结构进行更新,同时更新sentinels 字典。

Sentinel为主服务器创建的实例结构中的sentinels字典中保存了Sentinel本身的信息它所监控的主服务器的其他Sentinel信息。sentinels字典中的键是其中一个Sentinel的名字,这个名字由ip:port组成,而sentinels字典中的值则是键所对应Sentinel的实例结构。

当Sentinel通过消息频道发现一个新的Sentinel时,它不仅会为新的Sentinel在sentinels字典中创建相应的实例结构,还会创建连向新的Sentinel连接,最终多个Sentinel互相连接,共同监控相应的主从服务器。

  1. 监控主从服务器下线状态
    在默认情况下,Sentinel会以每秒一次的频率向他监控的主从服务器及其他Sentinel发送一条PING命令,并通过返回的PING命令回复来判断主从服务器及其他Sentinel是否已经下线,PING命令有效的回复有三种,
  • 返回PONG:表示网络连接正常。
  • 返回LOADING:发生错误
  • 返回MASTERDOWN:发生错误
    除此之外,如果在指定时间内没有得到回复,Sentinel都会认为服务器返回的回复是无效回复。
  1. 完成故障转移

当一台主服务器被判断为客观下线时,监控这台下线主服务器的多个Sentinel会进行协商,采用用一定的规则和方法选举出一个Sentinel领导,并由这个Sentinel领导对下线的服务器进行故障转移操作。

那为什么要选举Sentinel领导呢?

因为只有一个Sentinel完成故障转移,当多个Sentinel同时监控一个或多个主从复制结构时,就需要选举出一个Sentinel领导来完成故障转移。

具体如下:

  • 一个主从复制结构可以由多个Sentinel监控,而多个Sentinel都有被选举为Sentinel领导的可能。在进行选举之后,无论选举成功与否,所有Sentinel的配置纪元的值都会自增一-次。

  • 在一个配置纪元里,所有Sentinel都有一次将某 个Sentinel设置为局部领导的机会,当设置局部领导以后,就不能再更改它的配置纪元了。

  • 每个Sentinel监控到主服务器客观下线后,都会要求其他Sentinel选举自己作为局部领导。

  • 当一个Sentinel (源Sentinel) 向另一个Sentinel (目标Sentinel) 发送SENTINEL is-master down-by-addr命令,同时命令中的runid是源Sentinel的运行ID时,表示源Sentinel要求目标Sentinel将前者设置为后者的局部领导。

  • 将Sentinel设置为局部领导的规则是:先到先得。谁先向目标Sentinel发送设置要求,谁就能成为目标Sentinel的局部领导,之后它将拒绝接受其他的所有设置要求。

  • 目标Sentinel 在接收到SENTINEL is-mater-down-by-addr 命令之后,将会向源Sentinel返回一个命令回复, 在回复的参数中,leader_runid 和leader_epoch 参数分别记录了目标Sentinel的领导Sentinel的运行ID和配置纪元。

  • 源Sentinel在接收到目标Sentinel返回的命令回复之后,会判断其中的leader_runid和leader _epoch参数与自己的运行ID及配置纪元是否相同,如果相同,就表示目标Sentinel将原Sentinel设置成了局部领导。

  • 在多个Sentinel中,如果某个Sentinel 被半数以上的Sentinel 设置成了局部领导Sentinel,那么这个局部领导Sentinel将会成为多个Sentinel 的领导。

  • Sentinel 领导的选举需要半数以上的Sentinel 的支持,并且每个Sentinel 在配置纪元里只能设置一次局部领导,因此,在一个配置纪元里,只能出现一个Sentinel 领导.

  • 假如在指定的时间内, 没有选举出一个Sentinel作为领导,那么各个Sentinel将会过一段时间再进行选举,直到选举出新的Sentinel领导为止。

Sentinel领导选举成功之后,它将对已经下线的主服务器执行故障转移操作。具体步骤如下。

  1. 当master出现问题时,多个Sentinel发现并确认master有问题,多个Sentinel的确认保证了公平性。

  2. 在Sentinel内部会选举出一个Sentinel 作为领导,让它完成相关工作。

  3. 作为领导的Sentinel会从这台已经下线的主服务器的所有从服务器中选出一个“合适”的slave节点作为新的master。让这个slave 节点执行SLAVEOF no one命令,让其成为master节点。

  4. 此时Sentinel 会通知其余的slave 成为新的master的slave,这些slave就会复制新的master数据,通过向其余的slave发送SLAVEOF命令来完成。

  5. 之后Sentinel会通知相应的客户端新的master是谁,这样就能避免客户端再去连接旧的master,从而导致读取数据失败的问题产生。

  6. 在这个过程中,Sentinel依然会监控旧的master,对其进行“关注”。当旧的master复活之后,就会让它成为新的master的slave节点,然后去复制新的masters数据。

最后

我们已经启动了三个Redis、6379(主)、6380(从)、6381(从),还启动了三个哨兵。
在这里插入图片描述
执行info sentinel命令,可以看到此时的主节点是6379这个端口,在这里插入图片描述
现在模拟下宕机,将6379这个节点终止,过段时间后在执行info sentinel命令,发现新的master节点已经切换到6380这个节点。
在这里插入图片描述
查看6380的从节点信息,发现只有一个6381。
在这里插入图片描述
最后在把6379启动,过段时间再次执行info replication,就会看到6379已经成为自己的从节点。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值