Redis 3.0官方文档翻译计划(14)
——高可用(下)
分割下的一致性(Consistency under partitions)
Redis Sentinel的配置是最终一致性的,所以每个分区会被统一到一个可用的更高
版本的配置。但是,在使用Sentinel的真实世界系统中有三个不同的角色:
- Redis实例。
- Sentinel实例。
- 客户端。
为了定义系统的行为,我们得考虑这三个角色。
下面是一个有三个节点的简单网络,每一个节点运行一个Redis实例和一个Sentinel实例:
在这个系统中,初始状态是Redis 3是主服务器,Redis 1和Redis 2是从服务器。分割发生了,隔断了老的主服务器。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之间的分歧:
class="java" name="code">min-slaves-to-write 1
min-slaves-max-lag 10
有了上面的配置(请查看Redis分发版本中自带的redis.conf 文件中的
注释获取更多的信息),扮演主服务器的Redis实例如果不能写入到至少一个从服务器,将会停止接受写请求。由于复制是异步的,不能写入的意思就是从服务器也是断开的,或者在指定的max-lag秒数没有发送异步回应(acknowledges)。
使用这个配置,上面
例子中的Redis 3在10秒钟之后变得不可用。当分割恢复了,Sentinel 3的配置将会统一为新的,客户端B可以获取合法的配置并且继续。
Sentinel的持久化状态(Sentinel persistent state)
Sentinel的状态被持久化在Sentinel的配置文件中。例如,每次创建(
领导者leader Sentinel)或者收到新的配置,主服务器会将配置连同配置纪元持久化到磁盘中。这意味着,停止和
重启Sentinel进程是安全的。
Sentinel重配置实例(Sentinel reconfiguration of instances)
即使没有故障转移在进行中,Sentinel也会一直尝试在被监控的实例上设置当前配置。尤其是:
- 声称要成为主服务器的从服务器(根据当前配置),会被配置为从服务器来复制当前主服务器。
- 连接到错误主服务器的从服务器,会被重新配置来复制正确的主服务器。
- 为了Sentinel重新配置从服务器,错误的配置必须要观察一段时间,一段大于用于广播新配置所使用的时间。
这防止了持有旧配置(例如,因为刚刚从分割中恢复)的Sentinel会尝试在收到变更之前改变从服务器的配置。
也要注意,一直尝试使用当前配置使得故障转移对分割具有更强的抵抗力的语义是什么:
- 被故障转移的主服务器当再次可用时被重新配置成从服务器。
- 被分割的从服务器在一旦可到达时被重新配置。
从服务器的选举和优先级(Slave selection and priority)
当Sentinel实例准备执行故障转移,也就是当主服务器处于ODOWN状态,并且Sentinel从大多数已知Sentinel实例收到了故障转移
授权,需要选择一个合适的从服务器。
从服务器的选择过程评估从服务器的以下信息:
- 1.从主服务器断开的时间。
- 2.从服务器的优先级。
- 3.已处理的复制偏移量。
- 4.运行ID。
一个从服务器被
发现从主服务器断开超过十倍于配置的主服务器
超时(down-after-milliseconds选项),加上从正在执行故障转移的Sentinel的角度来看主服务器也不可用的时间,将会被认为不适合用于故障转移并跳过。
更严谨地说,一个从服务器的INFO输出表明已从主服务器断开超过:
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
就被认为不可靠并且被抛弃。
从服务器选择只考虑通过了上面测试的从服务器,并且基于上面的标准排序,使用下面的顺序。
- 1.从服务器按照Redis实例的redis.conf文件中配置的slave-priority排序。更低的优先级更偏爱。
- 2.如果优先级相同,将检查已处理的复制偏移量,从主服务器收到更多数据的从服务器将被选择。
- 3.如果多个从服务器有相同的优先家,并且从主服务器处理完相同的数据,将执行进一步的检查,选择按照字典顺序具有更小运行ID的从服务器。拥有较小的运行ID对从服务器并不是一个真正的优势,但是有助于从服务器选举过程更具有确定性,而不是随机选择一个。
如果对机器有强烈的偏好的话,Redis主服务器(故障转移以后成为从服务器)和从服务器都需要配置slave-priority。否则,所有的实例都可以使用默认的运行ID来运行(这是建议的设置,因为按照复制偏移量来选择从服务器要有趣得多)。
Redis实例可以配置一个特殊的slave-priority值0,这样就一定不会被Sentinel选举为新的主服务器。但是,按照这样配置的从服务器仍然会被Sentinel重新配置,从而在故障转移后复制新的主服务器,唯一的区别是永远不会变成主服务器。
Sentinel和Redis身份验证(authentication)
当主服务器被配置为需要客户端传递密码时,作为安全措施,从服务器也需要知道这个密码来验证主服务器,并且创建用于异步复制
协议的主从连接。
使用下面的配置指令完成:
- 主服务器中的requirepass用来设置密码验证,以确保实例不会处理没有验证过的客户端的请求。
- 从服务器中的masterauth用于从服务器验证主服务器,以正确的从其复制数据。
当使用Sentinel就没有单一的主服务器,因为故障转移以后从服务器可以扮演主服务器的角色,老的主服务器被重新配置以扮演从服务器,所以你要做的就是在你所有的主服务器和从服务器实例中设置以上指令。
这通常是一种逻辑上健全的设置,因为你不想只是保护主服务器中的数据,从服务器中也应拥有同样可访问的数据。
但是,在一些不常见的情况下,你需要从服务器无需验证就能访问,你仍可以通过设置从服务器的优先级为0(这将不允许从服务器被提升为主服务器),只为从服务器配置masterauth指令,不配置requirepass指令这样来做到,这样数据就可以让未经验证的客户端读取。
Sentinel API
Sentinel运行默认使用TCP端口26379(注意,6379是正常的Redis端口)。Sentinel接受使用Redis协议的命令,所以你可以使用redis-cli或者任何其他未修改的Redis客户端与Sentinel对话。
有两种方式与Sentinel对话:可以直接查询它来检查被监控的Redis实例的状态,看看它知道的其他Sentinel,等等。
另外一种方式是使用发布订阅,每当某个事件发生时,例如故障转移,或者一个实例进入到了一个错误条件,等等,接收从Sentinel推过来的通知。
Sentinel命令
下面是可接受的命令清单:
- PING:这个命令仅仅返回PONG。
- SENTINEL masters:展示被监控的主服务器列表及其状态。
- SENTINEL master <master name>:展示指定主服务器的状态和信息。
- SENTINEL slaves <master name>:展示指定主服务器的从服务器列表及其状态。
- SENTINEL get-master-addr-by-name <master name>:根据名字返回主服务器的IP地址和端口号。如果这台主服务器正在故障转移过程中或者成功结束了,返回被提升的从服务器的IP地址和端口。
- SENTINEL reset <pattern>:这个命令根据匹配的名字重置所有主服务器。pattern参数是通配符风格(glob-style)。重置进程清除主服务器的任何先前状态(包括进行中的故障转移),移除每一个主服务器上被发现和关联的从服务器和Sentinel。
- SENTINEL failover <master name> 当主服务器不可达时强制故障转移,无需要求其他的Sentinel同意(但是会发布一个新的配置版本,这样其他Sentinel就会更新它们的配置)。
运行时重配置Sentinel(Reconfiguring Sentinel)
从Redis 2.8.4开始,Sentinel提供了用于添加,删除和改变指定主服务器配置的API。注意,如果你有多个Sentinel实例,你得将改变应用到所有的Redis Sentinel实例才能运转正常。也就是说,改变一个Sentinel的配置不会自动传播到网络中的其它Sentinel。
下面是SENTINEL的子命令清单,用于更新Sentinel实例的配置。
- SENTINEL MONITOR <name> <ip> <port> <quorum>:这个命令告诉Sentinel开始监控一个指定名字,IP地址,端口和仲裁人数的新主服务器。这等同于sentinel.conf配置文件中的sentinel monitor配置指令,不同之处在于此处不能使用主机名作为IP地址,你需要提供一个IPv4或者Ipv6地址。
- SENTINEL REMOVE <name>:用于删除指定主服务器:主服务器不再被监控,完全从Sentinel内部状态中移除,所以不会被SENTINEL masters列出,等等。
- SENTINEL SET <name> <option> <value>:命令SET非常类似于Redis的CONFIG SET命令,用于改变指定主服务器的配置参数。可以指定多个选项-值对(或者根本啥都没有)。所有可以通过sentinel.conf配置的配置参数都可以通过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以一种易于
解析的格式(作为一个字段-值对数组)提供了所有的配置参数。
添加和删除Sentinel(Adding or removing Sentinels)
因为Sentinel实现的自动发现机制,添加一个新的Sentinel到你的部署中是一个很简单的过程。所有你需要干的就是启动一个配置用于监控当前活跃主服务器的Sentinel。在10秒钟之内,Sentinel就会获得其他Sentinel的列表以及连接到主服务器的从服务器集合。
如果你想一次添加多个新的Sentinel,建议一个一个的添加,等待所有其他的Sentinel知道了第一个再添加另一个。这在当添加新Sentinel的过程中发生错误时,仍然保证在分割的一侧能达到大多数时很有用。
在没有网络分割时,这可以通过添加每个新的Sentinel时带30秒的延迟来轻易实现。
在最后,可以使用命令SENTINEL MASTER mastername来检查是否所有的Sentinel就监控主服务器的Sentinel数量达成一致。
删除一个Sentinel要稍微复杂一些:Sentinel永远不会忘记已经发现的Sentinel,即使
他们在很长一段时间内都不可达,因为我们不想动态改变用于授权故障转移所需要的大多数以及创建新的配置版本。所以在没有网络分割情况下,需要执行下面的步骤来删除Sentinel:
- 1.停止你想删除的Sentinel的进程。
- 2.发送SENTINEL RESET *命令到所有其他的Sentinel实例(如果你想重置单个主服务器可以使用精确的主服务器名来代替*)。一个一个的来,前后等待至少30秒。
- 3.通过检查每个SENTINEL MASTER mastername的输出,来检查所有的Sentinel就当前活跃的Sentinel数量达成一致。
删除旧的主服务器或不可达从服务器(unreachable)
Sentinel不会忘记主服务器的从服务器,即使在很长时间内都不可达。这很有用,因为这样Sentinel能够在网络分割或者错误事件恢复后正确地重新配置一个返回的从服务器。
此外,故障转移之后,被故障转移的主服务器事实上被添加为新主服务器的从服务器,这样一旦恢复重新可用,就会被重新配置来复制新的主服务器。
但是,有时候你想从Sentinel监控的从服务器列表中永久删除一个从服务器(可能是旧的主服务器)。
要做到这个,你需要发送SENTINEL RESET mastername命令到所有的Sentinel:在接下来的10秒内,他们会刷新从服务器列表,只添加当前主服务器INFO输出中的正确复制的清单。
发布和订阅消息(Pub/Sub Messages)
客户端可以将Sentinel作为一个Redis兼容的发布订阅服务器(但是你不能使用PUBLISH)来使用,来订阅或者发布到频道,获取指定事件通知。
频道名称与事件名称是一样的。例如,名为+sdown的频道会收到所有关于实例进入SDOWN条件的通知。
简单使用PSUBSCRIBE *订阅来获得所有的消息。
下面是频道的清单,以及使用这个API你会收到的消息格式。第一个
单词是频道/事件名称,剩下的是数据的格式。
注意:指定instance details的地方表示提供了下面用于表示目标实例的参数:
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
标识主服务器的部分(从@参数到结束)是可选的,只在实例不是主服务器本身时指定。
- +reset-master <instance details>:主服务器被重置。
- +slave <instance details>:一个新的从服务器被发现和关联。
- +failover-state-reconf-slaves <instance details>:故障转移状态切换为reconf-slaves状态。
- +failover-detected <instance details>:另一个Sentinel启动了故障转移,或者任何其它外部实体被发现(关联的从服务器变为主服务器)。
- +slave-reconf-sent <instance details>:领导者Sentinel发送了SLAVEOF命令到这个实例,重新配置为新的从服务器。
- +slave-reconf-inprog <instance details>:从服务器正在重新配置为新的主服务器的从服务器,但是同步过程尚未完成。
- +slave-reconf-done <instance details>:从服务器完成了与新主服务器的同步。
- -dup-sentinel <instance details>:由于重复,指定主服务器的一个或多个Sentinel被移除。
- +sentinel <instance details>:这个主服务器的新的Sentinel被发现和关联。
- +sdown <instance details>:指定的实例处于主观下线状态。
- -sdown <instance details>:指定的实例不再处于主观下线状态。
- +odown <instance details>:指定的实例处于客观下线状态。
- -odown <instance details>:指定的实例不再处于客观下线状态。
- +new-epoch <instance details>:当前纪元被更新。
- +try-failover <instance details>:新的故障转移进行中,等待被大多数选中。
- +elected-leader <instance details>:赢得指定纪元的选举,可以进行故障转移。
- +failover-state-select-slave <instance details>:新的故障转移状态是select-slave:正在寻找合适的从服务器来提升。
- no-good-slave <instance details>:没有合适的从服务器来提升。一段时间后会重试,或者干脆放弃故障转移。
- selected-slave <instance details>:找到合适的从服务器来提升。
- failover-state-send-slaveof-noone <instance details>:正在重新配置将提升的从服务器为主服务器,等待完成后切换。
- failover-end-for-timeout <instance details>:故障转移由于超时而终止, 无论如何从服务器最终被配置为复制新的主服务器。
- failover-end <instance details>:故障转移顺利完成。所有从服务器被重配置为复制新主服务器。
- switch-master <master name> <oldip> <oldport> <newip> <newport>:配置变更后主服务器的IP和地址都是指定的。这是大多数外部用户感兴趣的消息。
- +tilt:进入tilt模式。
- -tilt:退出tilt模式。
TILT模式
Redis Sentinel严重依赖于计算机时间:例如,为了了解一个实例是否可用,Sentinel会记住最近成功回复PING命令的时间,与当前时间对比来了解这有多久。
但是,如果计算机时间以不可预知的方式改变了,或者计算机非常繁忙,或者某些原因进程阻塞了,Sentinel可能会开始表现得不可预知。
TILT模式是一个特别的保护模式,当发现一些会降低系统可靠性的奇怪问题时,Sentinel就会进入这种模式。Sentinel的定时中断通常每秒钟执行10次,所以我们期待两次定时中断调用之间相隔100毫秒左右。
Sentinel做的就是记录上一次定时中断调用的时间,与当前调用进行比较:如果时间差是负数或者出乎意料的大(2秒或更多),就进入了TILT模式(或者如果已经进入了,退出TILT模式将被推迟)。
当处于TILT模式时,Sentinel会继续监控一切,但是:
- 停止一切动作。
- 开始回复负数给SENTINEL is-master-down-by-addr请求,因为检测失败的能力不再可信了。
如果一切表现正常了30秒,将退出TILT模式。
处理-BUSY状态
(警告:还未实现)
当
脚本运行超过配置的脚本限制时间时返回-BUSY错误。当这种情况发生时,在触发故障转移之前Redis Sentinel会尝试发送SCRIPT KILL命令,这只有在脚本是只读的情况下才能成功。
Sentinel客户端实现
Sentinel需要显式的客户端支持,除非系统被配置为执行一个脚本,来实现透明重定向所有请求到新的主服务器实例(虚拟IP或其它类似系统)。客户端库实现的主题在Sentinel客户端指引手册中讨论(请期待本系列后续文档,译者注)。
===============================================================================
大家好,我是阮威。华中科技大学,计算机软件专业硕士。
毕业后加入腾讯,先后在腾讯电子商务部和无线游戏产品部工作,现供职于欢聚时代基础产品部。IT男,至今。欢迎大家收听我的公众账号。