Redis3官方文档(16)
——高可用(下)
——高可用(下)
3 Sentinel API
Sentinel提供了API来了解其自身状态,检查被监控的主服务器和从服务器的健康状态,订阅以接收特定的通知,并且在运行时变更Sentinel配置。
Sentinel默认运行于TCP的26389端口(注意6379是正常的Redis端口)。Sentinel接收Redis协议的命令,所以你可以使用Redis-cli或者任何没有修改多的Redis客户端来与Sentinel对话。
可以直接询问Sentinel从自身角度来检查被监控Redis实例的状态,了解所知道的其它Sentinel,等等。另外,使用发布/订阅(Pub/Sub)可以从Sentinel接收推送风格的通知,每当事件发生时,例如一次故障转移,或者一个实例进入一种错误条件,等等。
Sentinel命令(Sentinel commands)
以下是可以被接受的命令清单,没有包括用于修改Sentinel配置的命令,这些稍后再做介绍。
- PING:这个命令仅仅返回PONG。
- SENTINEL masters:展示被监控的主服务器列表及其状态。
- SENTINEL master
:展示指定主服务器的状态和信息。 - SENTINEL slaves
:展示指定主服务器的从服务器列表及其状态。 - SENTINEL sentinels
:展示指定主服务器的Sentinel列表及其状态。 - SENTINEL get-master-addr-by-name
:根据名字返回主服务器的IP地址和端口号。如果这台主服务器正在故障转移过程中或者成功结束了,返回被提升的从服务器的IP地址和端口。 - SENTINEL reset
:这个命令根据匹配的名字重置所有主服务器。pattern参数是通配符风格(glob-style)。重置进程清除主服务器的任何先前状态(包括进行中的故障转移),移除每一个主服务器上被发现和关联的从服务器和Sentinel。 - SENTINEL failover
:当主服务器不可达时强制故障转移,无需要求其他的Sentinel同意(但是会发布一个新的配置版本,这样其他Sentinel就会更新它们的配置)。 - SENTINEL ckquorum
:检查当前Sentinel的配置是否可以达到需要的仲裁人数来故障转移一个主服务器,以及是否可以达到需要的大多数来授权故障转移。这个命令应该被用于监控系统来检查Sentinel的部署是否正常。 - SENTINEL flushconfig:强制Sentinel重写其配置到磁盘,包括当前的Sentinel状态。通常情况下,每当状态(状态的子集的上下文,持久化于磁盘用于重启)变化时Sentinel重写配置。但是,有时候配置文件由于操作错误,磁盘故障,包升级脚本或者配置管理器等原因而丢失。这些情况下强制Sentinel重写配置的方式就很有用。这个命令在之前的配置文件完前丢失的情况下也能运转。
运行时重配置(Reconfiguring Sentinel at Runtime)
从Redis 2.8.4开始,Sentinel提供了用于添加,删除和改变指定主服务器配置的API。注意,如果你有多个Sentinel实例,你得将变更应用到所有的Redis Sentinel实例才能运转正常。也就是说,改变一个Sentinel的配置不会自动传播到网络中的其它Sentinel。
下面是SENTINEL的子命令清单,用于更新Sentinel实例的配置。
- SENTINEL MONITOR
:这个命令告诉Sentinel开始监控一个指定名字,IP地址,端口和仲裁人数的新主服务器。这等同于sentinel.conf配置文件中的sentinel monitor配置指令,不同之处在于此处不能使用主机名作为IP地址,你需要提供一个IPv4或者Ipv6地址。 - SENTINEL REMOVE
:用于删除指定主服务器:主服务器不再被监控,完全从Sentinel内部状态中移除,所以不会被SENTINEL masters列出,等等。 - SENTINEL SET
下面是SENTINEL SET命令的一个例子,用于修改一个名为objects-cache的主服务器的down-after-milliseconds配置:
SENTINEL SET objects-cache-master down-after-milliseconds 1000
启动以后,SENTINEL SET能用于设置所有在启动配置文件中可设置的配置参数。此外,还可以仅仅只改变主服务器的仲裁人数配置,而不需要使用SENTINEL REMOVE和SENTINEL MONITOR来删除和重新添加主服务器,而只需要:
SENTINEL SET objects-cache-master quorum 5
注意,没有与之等价的GET命令,因为SENTINEL MASTER以一种易于解析的格式(作为一个字段-值对数组)提供了所有的配置参数。
添加和删除(Adding or removing Sentinels)
因为Sentinel实现的自动发现机制,添加一个新的Sentinel到你的部署中是一个很简单的过程。所有你需要干的就是启动一个配置用于监控当前活跃主服务器的Sentinel。在10秒钟之内,Sentinel就会获得其他Sentinel的列表以及连接到主服务器的从服务器集合。
如果你想一次添加多个新的Sentinel,建议一个一个的添加,等待所有其他的Sentinel知道了第一个再添加另一个。这在当添加新Sentinel的过程中发生错误时,仍然保证在分割的一侧能达到大多数时很有用。
在没有网络分割时,这可以通过添加每个新的Sentinel时带30秒的延迟来轻易实现。
在最后,可以使用命令SENTINEL MASTER mastername来检查是否所有的Sentinel就监控主服务器的Sentinel数量达成一致。
删除一个Sentinel要稍微复杂一些:Sentinel永远不会忘记已经发现的Sentinel,即使他们在很长一段时间内都不可达,因为我们不想动态改变用于授权故障转移所需要的大多数以及创建新的配置版本。所以在没有网络分割情况下,需要执行下面的步骤来删除Sentinel:
- 停止你想删除的Sentinel的进程。
- 发送SENTINEL RESET *命令到所有其他的Sentinel实例(如果你想重置单个主服务器可以使用精确的主服务器名来代替*)。一个一个的来,前后等待至少30秒。
- 通过检查每个SENTINEL MASTER mastername的输出,来检查所有的Sentinel就当前活跃的Sentinel数量达成一致。
删除旧的主服务器或不可达从服务器(Removing the old master or unreachable slaves)
Sentinel不会忘记主服务器的从服务器,即使在很长时间内都不可达。这很有用,因为这样Sentinel能够在网络分割或者错误事件恢复后正确地重新配置一个返回的从服务器。
此外,故障转移之后,被故障转移的主服务器事实上被添加为新主服务器的从服务器,这样一旦恢复重新可用,就会被重新配置来复制新的主服务器。
但是,有时候你想从Sentinel监控的从服务器列表中永久删除一个从服务器(可能是旧的主服务器)。
要做到这个,你需要发送SENTINEL RESET mastername命令到所有的Sentinel:在接下来的10秒内,他们会刷新从服务器列表,只添加当前主服务器INFO输出中的正确复制的清单。
发布和订阅消息(Pub/Sub Messages)
客户端可以将Sentinel作为一个Redis兼容的发布/订阅服务器(但是你不能使用PUBLISH)来使用,来订阅或者发布到频道,获取指定事件通知。
频道名称与事件名称是一样的。例如,名为+sdown的频道会收到所有关于实例进入SDOWN条件的通知(SDOWN意味着从Sentinel的角度来看实例已经不再可达)。
简单使用PSUBSCRIBE *订阅来获得所有的消息。
下面是频道的清单,以及使用这个API你会收到的消息格式。第一个单词是频道/事件名称,剩下的是数据的格式。
注意:指定instance details的地方表示提供了下面用于表示目标实例的参数:
@
标识主服务器的部分(从@参数到结束)是可选的,只在实例不是主服务器本身时指定。
- +reset-master
:主服务器被重置。 - +slave
:一个新的从服务器被发现和关联。 - +failover-state-reconf-slaves
:故障转移状态切换为reconf-slaves状态。 - +failover-detected
:另一个Sentinel启动了故障转移,或者任何其它外部实体被发现(关联的从服务器变为主服务器)。 - +slave-reconf-sent
:领导者Sentinel发送了SLAVEOF命令到这个实例,重新配置为新的从服务器。 - +slave-reconf-inprog
:从服务器正在重新配置为新的主服务器的从服务器,但是同步过程尚未完成。 - +slave-reconf-done
:从服务器完成了与新主服务器的同步。 - -dup-sentinel
:由于重复,指定主服务器的一个或多个Sentinel被移除。 - +sentinel
:这个主服务器的新的Sentinel被发现和关联。 - +sdown
:指定的实例处于主观下线状态。 - -sdown
:指定的实例不再处于主观下线状态。 - +odown
:指定的实例处于客观下线状态。 - -odown
:指定的实例不再处于客观下线状态。 - +new-epoch
:当前纪元被更新。 - +try-failover
:新的故障转移进行中,等待被大多数选中。 - +elected-leader
:赢得指定纪元的选举,可以进行故障转移。 - +failover-state-select-slave
:新的故障转移状态是select-slave:正在寻找合适的从服务器来提升。 - no-good-slave
:没有合适的从服务器来提升。一段时间后会重试,或者干脆放弃故障转移。 - selected-slave
:找到合适的从服务器来提升。 - failover-state-send-slaveof-noone
:正在重新配置将提升的从服务器为主服务器,等待完成后切换。 - failover-end-for-timeout
:故障转移由于超时而终止,无论如何从服务器最终被配置为复制新的主服务器。 - failover-end
:故障转移顺利完成。所有从服务器被重配置为复制新主服务器。 - switch-master
:配置变更后主服务器的IP和地址都是指定的。这是大多数外部用户感兴趣的消息。 - +tilt:进入tilt模式。
- -tilt:退出tilt模式。
处理-BUSY状态(Handling of -BUSY state)
当Lua脚本运行超过配置的Lua脚本时间限制时Redis返回-BUSY错误。当这种情况发生时,在触发故障转移之前Redis Sentinel会尝试发送SCRIPT KILL命令,这只有在脚本是只读的情况下才能成功。
如果尝试后实例仍然处于错误条件中,它将会被最终故障转移。
从服务器优先级(Slaves priority)
Redis实例有一个被称为slave-priority的配置参数。这个信息暴露在Redis从服务器实例的INFO输出中,Sentinel会使用这个信息从这些从服务器中挑选一个来用于故障转移主服务器:
- 如果从服务器优先级被设置为0,那么这个从服务器将永远不会被提升为主服务器。
- 拥有更低优先级值的从服务器更倾向于被Sentinel选中。
例如,如果有一个从服务器S1与当前主服务器处于同一个数据中心,另一个从服务器S2在另一个数据中心,那么可以设置S1的优先级为10,S2的优先级为100,这样如果主服务器故障了,并且S1和S2都可用,那么S1将会被优先选择。
更多关于从服务器选择的信息,请查看本文档从服务器选择和优先级这一部分。
身份验证(Sentinel and Redis authentication)
当主服务器被配置为需要客户端传递密码时,作为安全措施,从服务器也需要知道这个密码来验证主服务器,并且创建用于异步复制协议的主从连接。
使用下面的配置指令完成:
- 主服务器中的requirepass用来设置验证密码,以确保实例不会处理没有验证过的客户端的请求。
- 从服务器中的masterauth用于从服务器验证主服务器,以正确的从其复制数据。
当使用Sentinel就没有单一的主服务器,因为故障转移以后从服务器可以扮演主服务器的角色,老的主服务器被重新配置以扮演从服务器,所以你要做的就是在你所有的主服务器和从服务器实例中设置以上指令。
这通常是一种逻辑上健全的设置,因为你不想只是保护主服务器中的数据,从服务器中也应拥有同样可访问的数据。
但是,在一些不常见的情况下,你需要从服务器无需验证就能访问,你仍可以通过设置从服务器的优先级为0(这将不允许从服务器被提升为主服务器),只为从服务器配置masterauth指令,不配置requirepass指令这样来做到,这样数据就可以让未经验证的客户端读取。
客户端实现(Sentinel clients implementation)
Sentinel需要显式的客户端支持,除非系统被配置为执行一个脚本,来实现透明重定向所有请求到新的主服务器实例(虚拟IP或其它类似系统)。客户端库实现的主题在Sentinel客户端指引手册中讨论(请期待本系列后续文档,译者注)。
4更多高级概念(More advanced concepts)
在下面的章节我们将会介绍一些Sentinel如何工作的细节,但不包含实现细节和算法,这些将在本文的最后再来介绍。
失败状态(SDOWN and ODOWN failure state)
Redis Sentinel有两个不同的下线(down)概念,一个被称为主观下线条件(SDOWN),另一个是本地Sentinel实例的下线条件。后者称为客观下线条件(ODOWN),当足够的Sentinel(至少为主服务器quorum参数配置的数量)满足SDOWN条件时就满足ODOWN条件,并且使用SENTINEL is-master-down-by-addr命令从其它Sentinel获得反馈。
从Sentinel的角度来看,如果我们没有在配置的is-master-down-after-milliseconds参数的指定时间内收到一个PING请求的合法响应,就达到了SDOWN的条件。
PING的可接受响应可以是以下其中之一:
- 回复+PONG。
- 回复-LOADING错误。
- 回复-MASTERDOWN错误。
其它回复(或者没有回复)都被认为是不合法的。但是注意,一个逻辑主服务器在其INFO输出中表达自己是一个从服务器则被认为是下线。
注意,SDOWN需要在配置的整个时间区间内没有收到可以接受的回复,例如,如果间隔配置为30000毫秒(30秒),我们每隔29秒收到一个可以接受的ping回复,实例被认为是正常工作的。
SDOWN不足以触发故障转移:它只是表示单个Sentinel相信Redis实例不可用。要触发一次故障转移,必须达到ODOWN条件。
从SDOWN切换到ODOWN没有使用强一致性算法,而仅仅是gossip的形式:如果一个指定的Sentinel在指定的时间范围内从足够多的Sentinel那里获得关于主服务器不工作的报告,SDOWN就被提升为ODOWN。如果这种应答(acknowledge)后面消失了,(ODOWN)标记就会被清除。
为了真正开始故障转移,需要大多数参与的更严格授权,但是,如果没有达到ODOWN状态,是不会触发故障转移的。
ODOWN条件只适用于主服务器。对于其他的实例,Sentinel不需要任何同意,所以从服务器和其它Sentinel永远都不会达到ODOWN状态,但是SDOWN可以。
但是SDOWN也有语义含义。例如,处于SDOWN的从服务器不会被执行故障转移的Sentinel选择进行提升。
自动发现(Sentinels and Slaves auto discovery)
Sentinel之间保持着连接来互相检查彼此的可用性,互相交换信息。但是你不需要在每个你运行的Sentinel实例中配置其他Sentinel的地址,因为Sentinel使用Redis主服务器的发布/订阅能力来发现监控相同的主服务器和从服务器的其他Sentinel。
这是通过向名为__sentinel__:hello频道发送问候消息(Hello Messages)实现的。
同样,你不需要配置连接在主服务器上的从服务器列表,因为Sentinel会通过询问Redis自动发现这个列表。
- 每个Sentinel向每个被监控的主服务器和从服务器的发布订阅频道__sentinel__:hello发送一条消息,每隔2秒,报告自己的存在状态:IP地址,端口号和runid。
- 每个Sentinel订阅了每个主服务器和从服务器的发布订阅频道__sentinel__:hello来寻找未知的Sentinel。当检测到新的Sentinel,就将其添加到这台主服务器的Sentinel列表中。
- 问候消息也包括主服务器当前的完整配置。如果接收Sentinel拥有一个比接收到的更老的主服务器配置,会立刻更新为新的配置。
- 在添加一个新的Sentinel到主服务器前,Sentinel总是检查是否已经有一个相同的runid或者相同地址(IP地址和端口对)的Sentinel。如果是的话,所有匹配的Sentinel将会被删除,新的被添加。
重配置(Sentinel reconfiguration of instances outside the failover procedure)
即使没有故障转移在进行中,Sentinel也会一直尝试在被监控的实例上设置当前配置。尤其是:
- 声称要成为主服务器的从服务器(根据当前配置),会被配置为从服务器来复制当前主服务器。
- 连接到错误主服务器的从服务器,会被重新配置来复制正确的主服务器。
为了Sentinel重新配置从服务器,错误的配置必须要观察一段时间,一段大于用于广播新配置所使用的时间。
这防止了持有旧配置(例如,因为刚刚从分割中恢复)的Sentinel会尝试在收到变更之前改变从服务器的配置。
也要注意,一直尝试使用当前配置使得故障转移对分割具有更强的抵抗力的语义是什么:
- 被故障转移的主服务器当再次可用时被重新配置成从服务器。
- 被分割的从服务器在一旦可到达时被重新配置。
关于这一部分要记住的重要一点是:Sentinel是一个系统,每一个进程总是尝试强加最后的逻辑配置给被监控的实例的集合。
选举和优先级(Slave selection and priority)
当Sentinel实例准备执行故障转移,也就是当主服务器处于ODOWN状态,并且Sentinel从大多数已知Sentinel实例收到了故障转移授权,需要选择一个合适的从服务器。
从服务器的选择过程评估从服务器的以下信息:
- 从主服务器断开的时间。
- 从服务器的优先级。
- 已处理的复制偏移量。
- 运行ID。
一个从服务器被发现从主服务器断开超过十倍于配置的主服务器超时(down-after-milliseconds选项)时间加上从正在执行故障转移的Sentinel的角度来看主服务器也不可用的时间,将会被认为不适合用于故障转移并跳过。
更严谨地说,一个从服务器的INFO输出表明已从主服务器断开超过:
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
就被认为不可靠并且被抛弃。
从服务器选择只考虑通过了上面测试的从服务器,并且基于上面的标准排序,使用下面的顺序。
- 从服务器按照Redis实例的redis.conf文件中配置的slave-priority排序。更低的优先级更偏爱。
- 如果优先级相同,将检查已处理的复制偏移量,从主服务器收到更多数据的从服务器将被选择。
- 如果多个从服务器有相同的优先级,并且从主服务器处理完相同的数据,将执行进一步的检查,选择按照字典顺序具有更小运行ID的从服务器。拥有较小的运行ID对从服务器并不是一个真正的优势,但是有助于从服务器选举过程更具有确定性,而不是随机选择一个。
如果对机器有强烈的偏好的话,Redis主服务器(故障转移以后最终变成从服务器)和从服务器都需要配置slave-priority。否则,所有的实例都可以使用默认的运行ID来运行(这是建议的设置,因为按照复制偏移量来选择从服务器要有趣得多)。
Redis实例可以配置一个特殊的slave-priority值0,这样就一定不会被Sentinel选举为新的主服务器。但是,按照这样配置的从服务器仍然会被Sentinel重新配置,从而在故障转移后复制新的主服务器,唯一的区别是永远不会变成主服务器。
5算法和内部(Algorithms and internals)
下面的章节我们将探索Sentinel行为的细节。用户并不完全需要知道所有的细节,但是对Sentinel的深入理解会帮助更有效的部署和操作Sentinel。
仲裁人数(Quorum)
本文前面的部分展示了每一个被Sentinel监控的主服务器都关联了一个quorum的配置。它指定了同意主服务器不可达或者错误条件需要的Sentinel进程数,以触发一次故障转移。
但是,故障转移被触发后,为了让故障转移真正执行,必须至少大多数的Sentinel授权某个Sentinel才能故障转移。处于只有少数Sentinel存在的分割中的Sentinel不会执行故障转移。
让我们搞得再清楚一些:
- quorum:检测错误条件以标记主服务器为ODOWN所需要的Sentinel的进程数。
- 故障转移由ODOWN状态触发。
- 一旦故障转移被触发,准备故障转移的Sentinel需要向大多数(或者大于大多数,如果仲裁人数设置为大于大多数的话)Sentinel请求授权。
差别看起来很微妙,但是实际上理解和使用起来都相当简单。例如,如果你有5个Sentinel实例,然后设置仲裁人数为2,只要有2个Sentinel认为主服务器不可达就会触发一次故障转移,这两个Sentinel仅当得到至少3个Sentinel的授权时才能故障转移。
如果设置仲裁人数为5,所有的Sentinel都必须同意主服务器的错误条件,故障转移需要所有Sentinel的授权。
这意味着quorum可以以两种方式用于调整Sentinel:
- 如果quorum的值设置为小于我们部署的Sentinel的大多数,我们就使得Sentinel对于主服务器的故障更敏感,只要哪怕只是少数的Sentinel无法与主服务器对话就会触发故障转移。
- 如果quorum的值设置为大于我们的Sentinel的大多数,我们使得只有当非常大(大于大多数)数量的连接完好的Sentinel就主服务器下线达成一致Sentinel才能故障转移。
配置纪元(Configuration epochs)
Sentinel需要从大多数获得授权来开启故障转移是有几个重要原因的:
当一个Sentinel得到授权了,就会为故障转移的主服务器获得一个唯一的配置纪元。这是在故障转移完成后用于标记新的配置的一个版本数字。因为大多数同意将一个指定的版本赋予一个指定的Sentinel,所以其它的Sentinel不能使用它。这意味着,每一次故障转移的配置都被一个唯一的版本标记。我们会看到为什么这个是如此的重要。
另外,Sentinel有一个规则:如果一个Sentinel投票给另一个Sentinel来故障转移指定的主服务器,将会等待一段时间后试图再次故障转移这台主服务器。这个延时(delay)是failover-timeout,你可以在sentinel.conf中配置。这意味着,Sentinel不会同时故障转移同一台主服务器,第一个请求被授权的将会尝试,如果失败了,过一会后另一个将会尝试,等等。
Redis Sentinel保证活性(liveness)属性,即如果大多数Sentinel能够对话,如果主服务器下线,最后会有一个被授权来故障转移。
Redis Sentinel也保证安全(safety)属性,即每个Sentinel将会使用不同的配置纪元来故障转移同一台主服务器。
配置传播(Configuration propagation)
一旦一个Sentinel能够成功故障转移一台主服务器,会开始广播新的配置,从而使其他Sentinel更新关于这台主服务器的信息。
为了认定故障转移是成功的,需要Sentinel能发送SLAVEOF NO ONE给选定的从服务器,并将其切换为主服务器,稍后可以在主服务器的INFO输出中观察到。
这时,即使从服务器的重新配置还在进行中,故障转移被认为是成功的,所有的Sentinel被要求开始报告新的配置。
新配置传播的方式,就是为什么我们需要每次Sentinel故障转移时被授权一个不同的版本号(配置纪元)的原因。
每一个Sentinel使用Redis的发布/订阅(Pub/Sub)消息不断地广播主服务器的配置版本,在主服务器上以及所有从服务器上。与此同时,所有的Sentinel等待其它Sentinel通知的配置消息。
配置信息在__sentinel__:hello频道中广播。
因为每一个配置有一个不同的版本号,所以更大的版本号总是胜过更小的版本号。
例如,一开始所有的Sentinel认为主服务器mymaster的配置为192.168.1.50:6379。这个配置拥有版本1。一段时间以后,一个Sentinel被授权以版本2来故障转移。如果故障转移成功,会广播一个新的配置,比如说192.168.1.50:9000,作为版本2。所有其他实例会看到这个配置,并相应地更新它们的配置,因为新的配置拥有一个更大的版本号。
这意味着,Sentinel保证第二个活性属性:一个可以相互通信的Sentinel集合会统一到同一个拥有更高版本号的配置。
基本上,如果网络是分割的,每个分区会统一到一个更高版本的本地配置。在没有分割的特殊情况下,只有一个分区,每个Sentinel将会配置一致。
分割下的一致性(Consistency under partitions)
Redis Sentinel的配置是最终一致性的,所以每个分割会被统一到一个可用的更高版本的配置。但是,在使用Sentinel的真实世界系统中有三个不同的角色:
- Redis实例。
- Sentinel实例。
- 客户端。
为了定义系统的行为,我们得考虑这三个角色。
下面是一个有三个节点的简单网络,每一个节点运行一个Redis实例和一个Sentinel实例:
在这个系统中,初始状态是Redis3是主服务器,Redis1和Redis2是从服务器。分割发生了,隔断了老的主服务器。Sentinel 1和2开始故障转移,提升Sentinel 1作为新的主服务器。
Sentinel的属性保证, Sentinel 1和2现在拥有主服务器的最新配置。但是,Sentinel 3仍是旧的配置,因为它存在于一个不同的分割中。
当网络分割恢复正常了,Sentinel 3将会更新其配置,但是,如果有客户端与老的主服务器被分割在一起,在分割期间会发生什么事情呢?
客户端会继续向Redis 3写,即老的主服务器。当分割又聚合在一起,Redis 3将会变成Redis 1的从服务器,分割期间所有写入的数据会丢失。
你可能想或者不想这种场景发生,取决于你的配置:
- 如果你将Redis用作缓存,客户端B可以继续往老的主服务器写,即使这些数据会丢失。
- 如果你将Redis用作存储,这样就不好了,你需要来配置系统以部分地阻止问题的发生。
因为Redis是异步复制,这种场景下没有完全阻止数据丢失的办法,但是你可以使用下面的Redis配置选项,来限制Redis 3和Redis 1之间的分歧:
min-slaves-to-write 1 min-slaves-max-lag 10
有了上面的配置(请查看Redis分发版本中自带的redis.conf 文件中的注释获取更多的信息),扮演主服务器的Redis实例如果不能写入到至少一个从服务器,将会停止接受写请求。由于复制是异步的,不能写入的意思就是从服务器也是断开的,或者在指定的max-lag秒数没有发送异步应答(acknowledges)。
使用这个配置,上面例子中的Redis 3在10秒钟之后变得不可用。当分割恢复了,Sentinel 3的配置将会统一为新的,客户端B可以获取有效的配置并且继续。
通常来说Redis+Sentinel作为一个整体是一个最终一致性系统,合并功能是最后一次故障转移获胜,旧的主服务器的数据将被丢弃,并从当前主服务器复制数据,所以总有一个丢失已应答写的窗口。注意,这不是Sentinel本身的局限,如果你用更健壮的一致性复制状态机来协调故障转移,同样的属性也适用。只有两种方式来避免丢失已应答的写:
- 使用同步复制(合适的一致性算法来运行复制状态机)。
- 使用一种最终一致性系统,同一对象的不同版本可以被合并。
Redis当前不能使用上面任何一种系统,这偏离了开发目标。但是有基于Redis存储的实现方案2的代理,如SoundCloud Roshi,或者Netflix Dynomite。
持久化状态(Sentinel persistent state)
Sentinel的状态被持久化在Sentinel的配置文件中。例如,每次创建(领导者leader Sentinel)或者收到新的配置,主服务器会将配置连同配置纪元持久化到磁盘中。这意味着,停止和重启Sentinel进程是安全的。
TILT模式(TILT mode)
Redis Sentinel严重依赖于计算机时间:例如,为了了解一个实例是否可用,Sentinel会记住最近成功回复PING命令的时间,与当前时间对比来了解这有多久。
但是,如果计算机时间以不可预知的方式改变了,或者计算机非常繁忙,或者某些原因进程阻塞了,Sentinel可能会开始表现得不可预知。
TILT模式是一个特别的保护模式,当发现一些会降低系统可靠性的奇怪问题时,Sentinel就会进入这种模式。Sentinel的定时中断通常每秒钟执行10次,所以我们希望两次定时中断调用之间相隔100毫秒左右。
Sentinel做的就是记录上一次定时中断调用的时间,与当前调用进行比较:如果时间差是负数或者出乎意料的大(2秒或更多),就进入了TILT模式(或者如果已经进入了,退出TILT模式将被推迟)。
当处于TILT模式时,Sentinel会继续监控一切,但是:
- 停止一切动作。
- 开始消极地回复SENTINEL is-master-down-by-addr请求,因为检测失败的能力不再可信了。
如果一切表现正常了30秒,将退出TILT模式。
注意,在有些情况下TITL模式可以被使用很多内核提供的单调始终API替换。但是还不能确定这是否是一个好的解决方案,因为当前系统万一进程只是被暂停(suspended)或者很长时间没有被调度器执行避免问题。
===============================================================================
大家好,我是阮威。华中科技大学,计算机软件专业硕士。毕业后加入腾讯,先后在腾讯电子商务部和无线游戏产品部工作,现供职于欢聚时代基础产品部。IT男,至今。欢迎大家收听我的公众账号。