对于现如今的IT运维来说,数据中心的应用和服务的高可用是重中之重,计划内或意外的宕机导致的业务停顿会直接造成经济损失,降低客户满意度进而破坏业内口碑,其重要性不言而喻。
高可用的系统要求部署可靠容错机制和运维手段,以便在出现单点故障时能及时检测并尽量降低业务中断时长,故障群集即为此目的应运而生。
在部署Windows Server 2012 R2故障转移群集之前,我们需要充分考虑集群所需的必要组件以便我们能够顺利的配置它。
集群仲裁( Quorum )用于定义当集群中至少有多少节点参与集群才能保证故障转移保护正常工作,每个节点都进行投票,票数满足仲裁设定则仲裁才可以开始或继续运行。当集群的节点数为偶数时,可以将集群配置为允许磁盘或者共享文件夹来作为见证( Witness )参与投票。为保证集群功能与性能正常,每个节点复制有包含投票数量的集群配置信息并会同步更新。
Windows Server 2012中,安装故障转移集群时,安装向导会自动选择默认仲裁模式,一旦集群安装完成,仲裁模式将被配置为如下两者之一,仲裁模式可在集群an'zhuang'wan'cheng'hou
从Windows Server 2012 R2 起,windows故障转移集群引入动态仲裁。在集群安装完成时,将默认使用动态仲裁模式。所谓动态仲裁模式,集群服务会根据集群中的节点数量动态修改集群仲裁模式,若节点数变为偶数,将启用磁盘见证( Witness ),使用节点磁盘多数仲裁;若节点数位奇数,将自动禁止磁盘见证( Witness )投票,使用节点多数仲裁,简而言之,动态仲裁会在节点故障时重新计算仲裁进而改变仲裁配置并保证集群正常提供服务。动态仲裁模式下,当新增或驱逐节点时,不需要手动配置仲裁模式。
Windows Server 2012 R2 还引入了强制仲裁弹性功能( force quorum resilience ),此功能被用在集群被拆分,集群节点互相无法感知的情况下,即脑裂( Split Brain )场景。用以强制使服务可用。
本例使用Window Server 2012 R2 搭建iSCSI存储
准备Target
当集群内某个节点出现问题时,需要通过一种健壮的方式保证识别出节点是否发生了故障。Redis集群内节点通过ping/pong消息实现节点通信,消息不但可以传播节点槽信息,还可以传播其他状态如:主从状态、节点故障等。因此故障发现也是通过消息传播机制实现的,主要环节包括: 主观下线(PFAIL-Possibly Fail) 和 客观下线(Fail)
一个节点认为某个节点失联了并不代表所有的节点都认为它失联了。所以集群还得经过一次协商的过程,只有当大多数节点都认定了某个节点失联了,集群才认为该节点需要进行主从切换来容错。Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了(PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。
集群中每个节点都会定期向其他节点发送ping消息,接收节点回复pong消息作为响应。如果在 cluster-node-timeout 时间内通信一直失败,则发送节点会认为接收节点存在故障,把接收节点标记为主观下线(PFail)状态
主观下线简单来讲就是,当 cluster-note-timeout 时间内某节点无法与另一个节点顺利完成ping消息通信时,则将该节点标记为主观下线状态
Redis集群对于节点最终是否故障判断非常严谨,只有一个节点认为主观下线并不能准确判断是否故障。当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播,通过Gossip消息传播,集群内节点不断收集到故障节点的下线报告。当 半数以上持有槽的主节点 都标记某个节点是主观下线时。触发客观下线流
程。
为什么必须是负责槽的主节点参与故障发现决策?
因为集群模式下只有处理槽的主节点才负责读写请求和集群槽等关键信息维护,而从节点只进行主节点数据和状态信息的复制。
为什么半数以上处理槽的主节点?
必须半数以上是为了应对网络分区等原因造成的集群分割情况,被分割的小集群因为无法完成从主观下线到
客观下线这一关键过程,从而防止小集群完成故障转移之后继续对外提供服务。
客观下线流程:
注意:
如果在 cluster-node-time*2 时间内无法收集到一半以上槽节点的下线报告,那么之前的下线报告将会过期,也就是说主观下线上报的速度追赶不上下线报告过期的速度,那么故障节点将永远无法被标记为客观下线从而导致
故障转移失败。因此不建议将 cluster-node-time 设置得过小
广播fail消息是客观下线的最后一步,它承担着非常重要的职责:
需要理解的是,尽管存在广播fail消息机制,但是集群所有节点知道故障节点进入客观下线状态是不确定的。比如当出现网络分区时有可能集群被分割为一大一小两个独立集群中。大的集群持有半数槽节点可以完成客观下线并广播fail消息,但是小集群无法接收到fail消息,网络分区会导致分割后的小集群无法收到大集群的fail消息,因此如果故障节点所有的从节点都在小集群内将导致无法完成后续故障转移,因此部署主从结构时需要根据自身机房/机架拓扑结构,降低主从被分区的可能性。
故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个替换它,从而保证集群的高可用。下线主节点的所有从节点承担故障恢复的义务,当从节点通过内部定时任务发现自身复制的主节点进入客观下线时,将会触发故障恢复流程
在集群定时任务 clusterCron 中,会遍历集群中的节点,对每个节点进行检查,判断节点是否下线。与节点下线相关的状态有两个,分别为 CLUSTER_NODE_PFAIL 和 CLUSTER_NODE_FAIL 。
CLUSTER_NODE_PFAIL :当前节点认为某个节点下线时,会将节点状态改为 CLUSTER_NODE_PFAIL ,由于可能存在误判,所以需要根据集群中的其他节点共同决定是否真的将节点标记为下线状态, CLUSTER_NODE_PFAIL 可以理解为疑似下线,类似哨兵集群中的主观下线 。
CLUSTER_NODE_FAIL :集群中有过半的节点标认为节点已下线,此时将节点置为 CLUSTER_NODE_FAIL 标记节点下线, CLUSTER_NODE_FAIL 表示节点真正处于下线状态,类似哨兵集群的客观下线 。
在集群定时任务遍历集群中的节点进行检查时,遍历到的每个节点记为 node ,当前节点记为 myself ,检查的内容主要有以下几个方面:
一、判断孤立主节点的个数
如果当前节点 myself 是从节点,正在遍历的节点 node 是主节点,并且 node 节点不处于下线状态,会判断孤立节点的个数,满足以下三个条件时,认定 node 是孤立节点,孤立节点个数增1:
二、检查连接
这一步主要检查和节点间的连接是否正常,有可能节点处于正常状态,但是连接有问题,此时需要释放连接,在下次执行定时任务时会进行重连,释放连接需要同时满足以下几个条件:
三、疑似下线判断
ping_delay 记录了当前时间距离向 node 节点发送PING消息的时间, data_delayd 记录了 node 节点向当前节点最近一次发送消息的时间,从ping_delay和data_delay中取较大的那个作为延迟时间。
如果延迟时间大于超时时间,判断 node 是否已经处于 CLUSTER_NODE_PFAIL 或者 CLUSTER_NODE_FAIL 状态,如果都不处于,将节点状态置为 CLUSTER_NODE_PFAIL ,认为节点疑似下线。
上述检查完成之后, 会判断当前节点是否是从节点,如果不处于 CLUSTER_MODULE_FLAG_NO_FAILOVER 状态,调用 clusterHandleSlaveFailover 处理故障转移,不过需要注意此时只是将节点置为疑似下线,并不满足故障转移条件,需要等待节点被置为FAIL下线状态之后,再次执行集群定时任务进入到 clusterHandleSlaveFailover 函数中才可以开始处理故障转移。
当前节点认为某个node下线时,会将node状态置为 CLUSTER_NODE_PFAIL 疑似下线状态,在定时向集群中的节点交换信息也就是发送PING消息时,消息体中记录了node的下线状态,其他节点在处理收到的PING消息时, 会将认为node节点下线的那个节点加入到node的下线链表fail_reports中,并调用 markNodeAsFailingIfNeeded 函数判断是否有必要将节点置为下线FAIL状态 :
markNodeAsFailingIfNeeded
markNodeAsFailingIfNeeded用于判断是否有必要将某个节点标记为FAIL状态:
clusterHandleSlaveFailover
由上面的内容可知,节点客观下线时会被置为 CLUSTER_NODE_FAIL 状态,下次执行集群定时任务时,在故障转移处理函数 clusterHandleSlaveFailover 中,就可以根据状态来检查是否需要执行故障转移。
不过在看 clusterHandleSlaveFailover 函数之前,先看一下 clusterState 中和选举以及故障切换相关的变量定义:
clusterHandleSlaveFailover函数中的一些变量
data_age : 记录从节点最近一次与主节点进行数据同步的时间 。如果与主节点处于连接状态,用当前时间减去最近一次与master节点交互的时间,否则使用当前时间减去与master主从复制中断的时间。
auth_age : 当前时间减去发起选举的时间 ,也就是距离发起选举过去了多久,用于判断选举超时、是否重新发起选举使用。
needed_quorum : quorum的数量,为集群中节点的数量的一半再加1 。
auth_timeout : 等待投票超时时间。
auth_retry_time : 等待重新发起选举进行投票的时间,也就是重试时间 。
一、故障转移条件检查
首先进行了一些条件检查,用于判断是否有必要执行故障转移,如果 处于以下几个条件之一,将会跳出函数,结束故障转移处理 :
二、主从复制进度校验
cluster_slave_validity_factor 设置了故障切换最大主从复制延迟时间因子,如果不为0需要校验主从复制延迟时间是否符合要求。
如果主从复制延迟时间 data_age 大于 mater向从节点发送PING消息的周期 + 超时时间 * 故障切换主从复制延迟时间因子 并且不是手动执行故障切换,表示主从复制延迟过大,不能进行故障切换终止执行。
三、是否需要重新发起选举
如果距离上次发起选举的时间大于超时重试时间,表示可以重新发起投票。
四、延迟发起选举
五、发起投票
如果满足执行故障的条件,接下来需从节点想集群中的其他节点广播消息,发起投票,不过只有主节点才有投票权。 failover_auth_sent 为0表示还未发起投票,此时开始发起投票:
六、执行故障切换
当某个节点获取到了集群中大多数节点的投票,即可进行故障切换,这里先不关注,在后面的章节会讲。
clusterGetSlaveRank用于计算当前节点的等级,遍历所属主节点的所有从节点,根据主从复制进度 repl_offset 计算, repl_offset 值越大表示复制主节点的数据越多,所以等级越高,对应的 rank 值就越低。
从节点在发起选举使用了 rank 的值作为延迟时间,值越低延迟时间越小,意味着选举优先级也就越高。
当从节点认为主节点故障需要发起投票,重新选举主节点时,在集群中广播了 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息,对应的处理在 clusterProcessPacket 函数中,里面会调用 clusterSendFailoverAuthIfNeeded 函数进行投票:
clusterSendFailoverAuthIfNeeded
clusterSendFailoverAuthIfNeeded函数用于进行投票,处理逻辑如下:
以上条件校验通过, 表示当前节点可以投票给发送请求的节点,此时更新 lastVoteEpoch ,记录最近一次投票的纪元(轮次),更新投票时间 node->slaveof->voted_time ,然后向发起请求的节点回复 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息。
主节点对发起投票请求节点的回复消息 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 同样在消息处理函数 clusterProcessPacket 中,会对发送回复消息的节点进行验证:
同时满足以上三个条件时, 表示发送者对当前节点进行了投票,更新当前节点记录的收到投票的个数, failover_auth_count 加1,此时有可能获取了大多数节点的投票,先调用 clusterDoBeforeSleep 设置一个 CLUSTER_TODO_HANDLE_FAILOVER 标记,在周期执行的时间事件中会调用对状态进行判断决定是否执行故障转移。
从节点收到投票后,会添加 CLUSTER_TODO_HANDLE_FAILOVER 标记,接下来看下对 CLUSTER_TODO_HANDLE_FAILOVER 状态的处理。
在 beforeSleep 函数(server.c文件中),如果开启了集群,会调用 clusterBeforeSleep 函数,里面就包含了对 CLUSTER_TODO_HANDLE_FAILOVER 状态的处理:
beforeSleep 函数是在Redis事件循环 aeMain 方法中被调用的,详细内容可参考 事件驱动框架源码分析 文章。
clusterBeforeSleep
在clusterBeforeSleep函数中,如果节点带有 CLUSTER_TODO_HANDLE_FAILOVER 标记,会调用 clusterHandleSlaveFailover 函数进行处理:
clusterHandleSlaveFailover 函数在上面我们已经见到过,这次我们来关注集群的故障转移处理。
如果当前节点获取了大多数的投票,也就是 failover_auth_count (得到的投票数量)大于等于 needed_quorum , needed_quorum 数量为集群中节点个数的一半+1,即可执行故障转移,接下来会调用 clusterFailoverReplaceYourMaster 函数完成故障转移。
clusterFailoverReplaceYourMaster
如果从节点收到了集群中过半的投票,就可以成为新的master节点,并接手下线的master节点的slot,具体的处理在clusterFailoverReplaceYourMaster函数中,主要处理逻辑如下:
总结
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)