redis哨兵模式是redis高可用模式中的一种方案他可以;从前一篇文章“主从数据拷贝”,我们可以知道,在多实例场景下,redis提供了数据同步的机制,但是,如果在主从模式下,主节点异常,那么整个redis服务实例只能提供查询服务,而不能够进行数据更新操作,除非有认为的干预,使程序恢复;
上图为redis哨兵模式的部署图;从上图中,我们可以看出,在哨兵模式下,至少需要一个sentinel(哨兵)实例和多个redis运行实例节点;
1、 sentinel实例的作用为监视其他redis运行实例;定时发送消息给各个redis运行实例,检测运行实例的状态,以及发送消息给与其有关联的其他sentinel实例,获取他们监控的实例的状态;从上体可以看出来,同一个redis运行实例节点可以有至少一个sentinel实例监视;各个sentilel实例之间相互关联,相互交换监控数据。
2、抛开sentinel实例看,可以看出各个运行实例其实是一个主从复制模式下的redis运行环境;redis运行实例分为两大类,master节点和slave节点,master节点进行数据的修改和数据查询,slave节点复制master节点的数据,并对外提供查询功能;
sentinel模式是redis的一种高可用方案,他可以在mstaer节点异常的时候,自动感知到并且从该master节点的从slave节点中自动选出一个节点,将他升级为master节点,继续进行工作,当异常节点恢复后,sentinel实例能够将异常节点设置成新选举出来的master节点的slave节点;
如此一来对外用户将感知不到redis运行实例中有节点异常过,以上是sentiel模式的总体思想;
接下来我们来分析一下程序的流程:
1、sentinel实例如何知道有哪些redis实例需要监控呢?
a、来自于sentinel模式的特有配置文件,在主配置文件开启sentinel模式之后,服务启动首先会识别到已经开启sentinel模式,这个时候,程序启动会去替换命令处理方法,使用sentinel特有的命令处理方法;在然后,程序会读取sentinel特有的配置文件,将配置文件中配置的节点信息读取出来,存储在内存中,并且设置好主观下线超时时间等关键信息;
b、另一部分信息来至于sentinel实例向被监控节点发送info消息时,接收到info消息的被监控节点会将本节点中所有的关联的实例信息发送给sentinel实例,sentinel实例在收到响应消息后,将其更新到自己维护的实例节点中;
c、还有一部分来自于节点的订阅;sentinel模式下,节点在启动的时候回都会想monitor订阅hello消息,其他的节点在发生某种事件(包含节点的加入和删除)后,会发送通知消息,这样,订阅过消息的节点就会收到对应的通知,如此就能够将节点信息相互传播。
2、sentinel实例是如何做到自动的维护redis主从节点的呢?
sentinel模式在经过初始化之后,接下来所有的处理都是在定时器中完成;接下来我们分析一下sentinel定时器的处理流程:
sentinel定时器的诸路口函数为sentinelTimer,该函数在redis定时器serverCron函数中被调用,每100ms调用一次;
接下来首先会判断sentinel实例是否进入到TITL模式,该模式是sentinel进行的自我检测,为了防止对redis节点的误判断,当sentinel检测到自己进入到TITL模式下之后,依然会收集信息被监视节点的信息,但不会进行状态判断和修改;
TITL自检结束之后,调用sentinelHandleDictOfRedisInstances方法,遍历整个sentinel监控的节点,以判断节点的健康状态;在sentinelHandleRedisInstance方法中。首先判断sentinel节点与被检测节点是否建立连接,如果没有检测连接,则创建连接,本节点与被每个被检测节点有量条连接,一条称之为cc(command connect),另一条称之为pc(publish command),sentinel节点与其他关联节点以及被检测节点的连接创建时基于hiredis下的接口创建的异步连接;
在连接建立成功之后,当被检测节点不是关联sentinel实例,并且没有发送过info消息或者上一个info消息已经超时,sentinel会向被检测节点发送info消息,被检测节点在收到请求消息后其关联的节点信息发送给sentinel节点,sentinel节点获取到响应消息后,更新消息收到消息的时间并将响应消息解析,已更新他自己维护的被检测节点的信息(sentinelInfoReplyCallback方法);当不满足info消息发送的条件下,会依次检测是否需要发送ping消息和publish消息;发送这些消息就是为获取被检测节点的信息,同时可以更新节点通信信息,为后续的检测做准备;这些功能sentinelSendPeriodicCommands方法中实现;
在获取到节点信息之后,需要检测节点是否是主观下线(站在本sentinel节点的角度,依赖刚才被检测节点信息获取时更新的时间,进行判断);在判断主观下线时,首先获取到目前为止,本节点与被检测节点上次通信的时间间隔,然后分别检测cc连接和pc连接是否是正常的,当连接不正常,则断开连接;然后判断从上次通信时间到当前时间是否已经超过下线超时时间,或者本节点自身激励的角色和其他节点上报的角色不同,当满足以上条件的话,则判定当前节点主观下线,并发送主观下线消息通知订阅了__sentinel__:hello消息的节点,让他们去更新自身维护的信息;实现在方法sentinelCheckSubjectivelyDown中;
如果当前检测的节点是主节点,则需要进行接下来的客观下线判断故障转移;
客观下线判断逻辑为:当前被检测节点如果被判断为主观下线,则遍历所有的监视被检测节点的的sentinel实例,统计判定被检测节点为下线的sentinel实例个数,如果sentinel实例个数大于配置的客观下线条件成立的数量,则判定该节点客观下线,并发送通知给其他sentinel实例。该方法是现在函数sentinelCheckObjectivelyDown中;
由于sentinel实例判定某一个被检测的主节点同时出于主动下线和被动下线状态,所以需要进行故障转移;
1、在某些环境下使用sentinel部署redis集群,为了保证集群的高可用,可能会同时部署多个sentinel实例,所以首先需要在这些sentinel实例中选举出进行故障转移的sentinel实例;首先遍历所有的监控被检测节点的sentinel实例(sentinelGetLeader方法),统计各个sentinel实例选举的执行故障转移sentinel实例,然后根据选举的结果,获取执行故障转移的sentinel实例的名称。如果选举出的实例是不是当前sentinel本身,则当前sentinel实例直接退出;否则执行下一步,并发送通知,高数各个订阅了消息的节点,sentinel实例正在选择主节点;
2、在当前的被检测的下线的master节点中,遍历从属于该master节点的所有slave节点,排除所有的下线节点、ping命令超时节点、与sentinel节点连接断开的slave节点、优先级为0的节点、info命令响应时间过长的节点以及复制拷贝超时过长的slave节点,然后从剩余的节点中,按照优先级、复制主程序数据量、runid等信息一次排序,选举出最优的slave节点,将其选做新的master节点。
3、接下来就是sentinel节点发送通知给其他的节点,将slave设置成新的master节点
到此,整个sentinel的处理流程已经介绍完成,部分具体细节,可以查看源码了解