Sentinel 介绍
Sentinel 作用
- Master 状态检测
- 如果 Master 异常,则会进行 Master-Slave 切换,将其中一个 Slave 作为 Master,将之前的 Master 作为 Slave。
- Master-Slave 切换后,
master_redis.conf、slave_redis.conf和sentinel.conf的内容都会发生改变,即master_redis.conf中会多一行 slaveof 的配置,sentinel.conf的监控目标会随之调换。
Sentinel 工作机制
Sentinel 状态持久化
Sentinel 的状态会被持久化地写入 Sentinel 的配置文件中。每次当收到一个新的配置时,或者新创建一个配置时,配置会被持久化到硬盘中,并带上配置的版本戳。这意味着,可以安全的停止和重启 Sentinel 进程。
Sentinel 定时任务
每个 Sentinel 实例都执行的定时任务
- 每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命令。
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过
own-after-milliseconds选项所指定的值,则这个实例会被 Sentinel 标记为主观下线。 - 如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态。
- 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认 Master 的确进入了主观下线状态,则 Master 会被标记为客观下线。
- 在一般情况下,每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令。
- 当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
- 若没有足够数量的 Sentinel 同意 Master 已经下线,Master 的客观下线状态就会被移除。若 Master 重新向 Sentinel 的 PING 命令返回有效回复,Master 的主观下线状态就会被移除。
三个定时任务
- 每 10 秒每个 Sentinel 会对 Master 和 Slave 执行 INFO 命令,这个任务达到两个目的:a)发现 Slave 节点 b)确认主从关系
- 每 2 秒每个 Sentinel 通过 Master 节点的 channel 交换信息(pub/sub)。Master 节点上有一个发布订阅的频道 (
__sentinel__:hello)。Sentinel 节点通过__sentinel__: hello频道进行信息交换 (对节点的”看法”和自身的信息),达成共识。 - 每 1 秒每个 Sentinel 对其他 Sentinel 和 Redis 节点执行 PING 操作(相互监控),这个其实是一个心跳检测,是失败判定的依据。
PING 操作的有效/合法回复
当 Sentinel 发送 PING 后,以下回复之一都被认为是合法的:
PING replied with +PONG.
PING replied with -LOADING error.
PING replied with -MASTERDOWN error.
其它任何回复(或者根本没有回复)都是不合法的。
主观下线 & 客观下线
主观下线
所谓主观下线(Subjectively Down,简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断,即单个 Sentinel 认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。
主观下线就是说如果服务器在 down-after-milliseconds 给定的毫秒数之内,没有有效回复,那么 Sentinel 将这个服务器标记为主观下线(SDOWN )。
Sentinel 会以每秒一次的频率向所有与其建立了命令连接的实例(master,从服务,其他 Sentinel)发 PING 命令,通过判断 PING 回复是有效回复,还是无效回复来判断实例是否在线(对该 Sentinel 来说是“主观在线”)。
Sentinel 配置文件中的 down-after-milliseconds 设置了判断主观下线的时间长度,如果实例在 down-after-milliseconds 毫秒内,返回的都是无效回复,那么 Sentinel 会认为该实例已(主观)下线,修改其 flags 状态为 SRI_S_DOWN。如果多个 Sentinel 监视一个服务,有可能存在多个 Sentinel 的 down-after-milliseconds 配置不同,这个在实际生产中要注意。
客观下线
客观下线(Objectively Down,简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断,并且通过 Sentinel is-master-down-by-addr 命令互相交流之后,得出的服务器下线判断,然后开启 failover。
客观下线就是说只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后,服务器才会被标记为客观下线(ODOWN)。
只有当 Master 被认定为客观下线时,才会发生故障迁移。
当 Sentinel 监视的某个服务主观下线后,Sentinel 会询问其它监视该服务的 Sentinel,看它们是否也认为该服务主观下线,接收到足够数量(这个值可以配置)的 Sentinel 判断为主观下线,就任务该服务客观下线,并对其做故障转移操作。
Sentinel 通过发送 Sentinel is-master-down-by-addr ip port current_epoch runid,( ip :主观下线的服务 id,port :主观下线的服务端口,current_epoch :Sentinel 的纪元,runid :表示检测服务下线状态,如果是 Sentinel 运行 id,表示用来选举领头 sentinel)来询问其它 Sentinel 是否同意服务下线。
一个 Sentinel 接收另一个 Sentinel 发来的 is-master-down-by-addr 后,提取参数,根据 ip 和端口,检测该服务时候在该 Sentinel 主观下线,并且回复 is-master-down-by-addr,回复包含三个参数:down_state (1 表示已下线,0 表示未下线),leader_runid (领头 Sentinel id),leader_epoch (领头 Sentinel 纪元)。
Sentinel 接收到回复后,根据配置设置的下线最小数量,达到这个值,既认为该服务客观下线。
客观下线条件只适用于 Master: 对于任何其他类型的 Redis 实例, Sentinel 在将它们判断为下线前不需要进行协商,所以 Slave 或者其他 Sentinel 永远不会达到客观下线条件。只要一个 Sentinel 发现某个 Master 进入了客观下线状态,这个 Sentinel 就可能会被其他 Sentinel 推选出,并对失效的主服务器执行自动故障迁移操作。
主观下线&客观下线的配置
Sentinel monitor <masterName> <ip> <port> <quorum>
masterName 这个是对某个 master+Slave 组合的一个区分标识(一套 Sentinel 是可以监听多套 master+Slave 这样的组合的)。ip 和 port 就是 Master 节点的 ip 和端口号。
quorum 这个参数是进行客观下线的一个依据,意思是至少有 quorum 个 Sentinel 主观的认为这个 Master 有故障,才会对这个 Master 进行下线以及故障转移。因为有的时候,某个 Sentinel 节点可能因为自身网络原因,导致无法连接 master,所以这就需要多个 Sentinel 都一致认为该 Master 有问题,这就保证了公平性和高可用。
Sentinel down-after-milliseconds <masterName> <timeout>
这个配置其实就是进行主观下线的一个依据,timeout 是一个毫秒值,表示:如果这台 Sentinel 超过 timeout 这个时间都无法连通 Master 包括 Slave(Slave 不需要客观下线,因为不需要故障转移)的话,就会主观认为该 Master 已经下线(实际下线需要客观下线的判断通过才会下线)。
多个 Sentinel 之间是如何达到共识
这就是依赖于前面说的第二个定时任务,某个 Sentinel 先将 Master 节点进行一个主观下线,然后会将这个判定通过 Sentinel is-master-down-by-addr 命令问对应的节点是否也同样认为该 addr 的 Master 节点要做客观下线。最后当达成这一共识的 Sentinel 个数达到前面说的 quorum 设置的这个值时,就会对该 Master 节点下线进行故障转移。quorum 的值一般设置为 Sentinel 个数的二分之一加 1,例如 3 个 Sentinel 就设置 2。
配置版本号
配置版本号
为什么要先获得大多数 Sentinel 的认可时才能真正去执行 failover 呢?
当一个 Sentinel 被授权后,它将会获得宕掉的 Master 的一份最新配置版本号,当 failover 执行结束以后,这个版本号将会被用于最新的配置。因为大多数 Sentinel 都已经知道该版本号已经被要执行 failover 的 Sentinel 拿走了,所以其他的 Sentinel 都不能再去使用这个版本号。这意味着,每次 failover 都会附带有一个独一无二的版本号。我们将会看到这样做的重要性。而且,Sentinel 集群都遵守一个规则:如果 Sentinel A 推荐 Sentinel B 去执行 failover,B 会等待一段时间后,自行再次去对同一个 Master 执行 failover,这个等待的时间是通过 failover-timeout 配置项去配置的。从这个规则可以看出,Sentinel 集群中的 Sentinel 不会再同一时刻并发去 failover 同一个 master,第一个进行 failover 的 Sentinel 如果失败了,另外一个将会在一定时间内进行重新进行 failover,以此类推。
Redis Sentinel 保证了活跃性:如果大多数 Sentinel 能够互相通信,最终将会有一个被授权去进行 failover.
Redis Sentinel 也保证了安全性:每个试图去 failover 同一个 Master 的 Sentinel 都会得到一个独一无二的版本号。
配置传播
一旦一个 Sentinel 成功地对一个 Master 进行了 failover,它将会把关于 Master 的最新配置通过广播形式通知其它 Sentinel,其它的 Sentinel 则更新对应 Master 的配置。
一个 faiover 要想被成功实行,Sentinel 必须能够向选为 Master 的 Slave 发送 SLAVEOF NO ONE 命令,然后能够通过 INFO 命令看到新 Master 的配置信息。
当将一个 slave 选举为 Master 并发送 SLAVEOF NO ONE 后,即使其它的 slave 还没针对新 Master 重新配置自己,failover 也被认为是成功了的,然后所有 Sentinels 将会发布新的配置信息。
新配在集群中相互传播的方式,就是为什么我们需要当一个 Sentinel 进行 failover 时必须被授权一个版本号的原因。
每个 Sentinel 使用发布/订阅的方式持续地传播 Master 的配置版本信息,配置传播的发布/订阅管道是:__sentinel__: hello。
因为每一个配置都有一个版本号,所以以版本号最大的那个为标准。
举个例子:
假设有一个名为 myMaster 的地址为 192.168.10.202:6379。一开始,集群中所有的 Sentinel 都知道这个地址,于是为 myMaster 的配置打上版本号 1。一段时候后 myMaster 死了,有一个 Sentinel 被授权用版本号 2 对其进行 failover。如果 failover 成功了,假设地址改为了 192.168.10.202:9000,此时配置的版本号为 2,进行 failover 的 Sentinel 会将新配置广播给其他的 sentinel,由于其他 Sentinel 维护的版本号为 1,发现新配置的版本号为 2 时,版本号变大了,说明配置更新了,于是就会采用最新的版本号为 2 的配置。
这意味着 Sentinel 集群保证了第二种活跃性:一个能够互相通信的 Sentinel 集群最终会采用版本号最高且相同的配置。
选举领头 Sentinel
Sentinel 的”仲裁会”
前面我们谈到,当一个 Master 被 Sentinel 集群监控时,需要为它指定一个参数,这个参数指定了当需要判决 Master 为不可用,并且进行 failover 时,所需要的 Sentinel 数量,可以称这个参数为票数。
不过,当 failover 主备切换真正被触发后,failover 并不会马上进行,还需要 Sentinel 中的大多数 Sentinel 授权后才可以进行 failover。
当 ODOWN 时,failover 被触发。failover 一旦被触发,尝试去进行 failover 的 sentinel 会去获得“大多数”sentinel 的授权(如果票数比大多数还要大的时候,则询问更多的 sentinel)
这个区别看起来很微妙,但是很容易理解和使用。例如,集群中有 5 个 sentinel,票数被设置为 2,当 2 个 Sentinel 认为一个 Master 已经不可用了以后,将会触发 failover,但是,进行 failover 的那个 Sentinel 必须先获得至少 3 个 Sentinel 的授权才可以实行 failover。
如果票数被设置为 5,要达到 ODOWN 状态,必须所有 5 个 Sentinel 都主观认为 Master 为不可用,要进行 failover,那么得获得所有 5 个 Sentinel 的授权。
Sentinel 集群
Sentinel 支持集群(可以部署在多台机器上,也可以在一台物理机上通过多端口实现伪集群部署)
很显然,只使用单个 sentinel 进程来监控 redis 集群是不可靠的,当 sentinel 进程宕掉后 (sentinel 本身也有单点问题,single-point-of-failure) 整个集群系统将无法按照预期的方式运行。所以有必要将 sentinel 集群,这样有几个好处:
1)即使有一些 sentinel 进程宕掉了,依然可以进行 redis 集群的主备切换;
2)如果只有一个 sentinel 进程,如果这个进程运行出错,或者是网络堵塞,那么将无法实现 redis 集群的主备切换(单点问题);
3)如果有多个 sentinel,redis 的客户端可以随意地连接任意一个 sentinel 来获得关于 redis 集群中的信息。
Sentinel 集群注意事项
1)只有 Sentinel 集群中大多数服务器认定 master 主观下线时 master 才会被认定为客观下线,才可以进行故障迁移,也就是说,即使不管我们在 sentinel monitor 中设置的数是多少,就算是满足了该值,只要达不到大多数,就不会发生故障迁移。
2)官方建议 sentinel 至少部署三台,且分布在不同机器。这里主要考虑到 sentinel 的可用性,假如我们只部署了两台 sentinel,且 quorum 设置为 1,也可以实现自动故障迁移,但假如其中一台 sentinel 挂了,就永远不会触发自动故障迁移,因为永远达不到大多数 sentinel 认定 master 主观下线了。
3)sentinel monitor 配置中的 master IP 尽量不要写 127.0.0.1 或 localhost,因为客户端,如 jedis 获取 master 是根据这个获取的,若这样配置,jedis 获取的 ip 则是 127.0.0.1,这样就可能导致程序连接不上 master
4)当 sentinel 启动后会自动的修改 sentinel. conf 文件,如已发现的 master 的 slave 信息,和集群中其它 sentinel 的信息等, 这样即使重启 sentinel 也能保持原来的状态。注意,当集群服务器调整时,如更换 sentinel 的机器,或者新配置一个 sentinel,请不要直接复制原来运行过得 sentinel 配置文件,因为其里面自动生成了以上说的那些信息,我们应该复制一个新的配置文件或者把自动生成的信息给删掉。
5)当发生故障迁移的时候,master 的变更记录与 slave 更换 master 的修改会自动同步到 redis 的配置文件,这样即使重启 redis 也能保持变更后的状态。
Sentinel 和 Slaves 的自动发现机制
虽然 sentinel 集群中各个 sentinel 都互相连接彼此来检查对方的可用性以及互相发送消息。但是你不用在任何一个 sentinel 配置任何其它的 sentinel 的节点。因为 sentinel 利用了 master 的发布/订阅机制去自动发现其它也监控了统一 master 的 sentinel 节点。
通过向名为 __sentinel__: hello 的管道中发送消息来实现。
同样,你也不需要在 sentinel 中配置某个 master 的所有 slave 的地址,sentinel 会通过询问 master 来得到这些 slave 的地址的。
每个 sentinel 通过向每个 master 和 slave 的发布/订阅频道 __sentinel__: hello 每秒发送一次消息,来宣布它的存在。
每个 sentinel 也订阅了每个 master 和 slave 的频道 __sentinel__: hello 的内容,来发现未知的 sentinel,当检测到了新的 sentinel,则将其加入到自身维护的 master 监控列表中。
每个 sentinel 发送的消息中也包含了其当前维护的最新的 master 配置。如果某个 sentinel 发现自己的配置版本低于接收到的配置版本,则会用新的配置更新自己的 master 配置。
在为一个 master 添加一个新的 sentinel 前,sentinel 总是检查是否已经有 sentinel 与新的 sentinel 的进程号或者是地址是一样的。如果是那样,这个 sentinel 将会被删除,而把新的 sentinel 添加上去。
故障转移
一次故障转移操作大致分为以下流程:
发现主服务器已经进入客观下线状态。
对我们的当前集群进行自增,并尝试在这个集群中当选。
如果当选失败,那么在设定的故障迁移超时时间的两倍之后,重新尝试当选。如果当选成功,那么执行以下步骤:
选出一个从服务器,并将它升级为主服务器。
向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为主服务器。
通过发布与订阅功能,将更新后的配置传播给所有其他 Sentinel ,其他 Sentinel 对它们自己的配置进行更新。
向已下线主服务器的从服务器发送 SLAVEOF 命令,让它们去复制新的主服务器。
当所有从服务器都已经开始复制新的主服务器时,领头 Sentinel 终止这次故障迁移操作。
每当一个 Redis 实例被重新配置(reconfigured) —— 无论是被设置成主服务器、从服务器、又或者被设置成其他主服务器的从服务器 —— Sentinel 都会向被重新配置的实例发送一个 CONFIG REWRITE 命令,从而确保这些配置会持久化在硬盘里。
Sentinel 自动故障迁移的一致性特质
Sentinel 自动故障迁移使用 Raft 算法来选举领头(leader) Sentinel ,从而确保在一个给定的纪元(epoch)里,只有一个领头产生。
这表示在同一个纪元中,不会有两个 Sentinel 同时被选中为领头,并且各个 Sentinel 在同一个纪元中只会对一个领头进行投票。
更高的配置纪元总是优于较低的纪元,因此每个 Sentinel 都会主动使用更新的纪元来代替自己的配置。
简单来说,可以将 Sentinel 配置看作是一个带有版本号的状态。一个状态会以最后写入者胜出(last-write-wins)的方式(也即是,最新的配置总是胜出)传播至所有其他 Sentinel 。
举个例子,当出现网络分割(network partitions)时,一个 Sentinel 可能会包含了较旧的配置,而当这个 Sentinel 接到其他 Sentinel 发来的版本更新的配置时, Sentinel 就会对自己的配置进行更新。
如果要在网络分割出现的情况下仍然保持一致性,那么应该使用 min-slaves-to-write 选项,让主服务器在连接的从实例少于给定数量时停止执行写操作,与此同时,应该在每个运行 Redis 主服务器或从服务器的机器上运行 Redis Sentinel 进程。
Sentinel 状态的持久化
Sentinel 的状态会被持久化在 Sentinel 配置文件里面。每当 Sentinel 接收到一个新的配置,或者当领头 Sentinel 为主服务器创建一个新的配置时,这个配置会与配置纪元一起被保存到磁盘里面。这意味着停止和重启 Sentinel 进程都是安全的。
Sentinel 在非故障迁移的情况下对实例进行重新配置
即使没有自动故障迁移操作在进行, Sentinel 总会尝试将当前的配置设置到被监视的实例上面。特别是:
根据当前的配置,如果一个从服务器被宣告为主服务器,那么它会代替原有的主服务器,成为新的主服务器,并且成为原有主服务器的所有从服务器的复制对象。
那些连接了错误主服务器的从服务器会被重新配置,使得这些从服务器会去复制正确的主服务器。
不过,在以上这些条件满足之后, Sentinel 在对实例进行重新配置之前仍然会等待一段足够长的时间,确保可以接收到其他 Sentinel 发来的配置更新,从而避免自身因为保存了过期的配置而对实例进行了不必要的重新配置。
总结来说,故障转移分为三个步骤:
1)从下线的主服务的所有从服务里面挑选一个从服务,将其转成主服务
sentinel 状态数据结构中保存了主服务的所有从服务信息,领头 sentinel 按照如下的规则从从服务列表中挑选出新的主服务;
删除列表中处于下线状态的从服务;
删除最近 5 秒没有回复过领头 sentinel info 信息的从服务;
删除与已下线的主服务断开连接时间超过 down-after-milliseconds*10 毫秒的从服务,这样就能保留从的数据比较新(没有过早的与主断开连接);
领头 sentinel 从剩下的从列表中选择优先级高的,如果优先级一样,选择偏移量最大的(偏移量大说明复制的数据比较新),如果偏移量一样,选择运行 id 最小的从服务。
2)已下线主服务的所有从服务改为复制新的主服务
挑选出新的主服务之后,领头 sentinel 向原主服务的从服务发送 slaveof 新主服务的命令,复制新 master。
3)将已下线的主服务设置成新的主服务的从服务,当其回复正常时,复制新的主服务,变成新的主服务的从服务
同理,当已下线的服务重新上线时,sentinel 会向其发送 slaveof 命令,让其成为新主的从。
温馨提示:还可以向任意 sentinel 发生 sentinel failover <masterName> 进行手动故障转移,这样就不需要经过上述主客观和选举的过程。
使用时配置
Sentinel 和 redis 身份验证
当一个 master 配置为需要密码才能连接时,客户端和 slave 在连接时都需要提供密码。
master 通过 requirepass 设置自身的密码,不提供密码无法连接到这个 master。
slave 通过 masterauth 来设置访问 master 时的密码。
但是当使用了 sentinel 时,由于一个 master 可能会变成一个 slave,一个 slave 也可能会变成 master,所以需要同时设置上述两个配置项。
Sentinel API
在默认情况下, Sentinel 使用 TCP 端口 26379 (普通 Redis 服务器使用的是 6379 )。Sentinel 接受 Redis 协议格式的命令请求,所以你可以使用 redis-cli 或者任何其他 Redis 客户端来与 Sentinel 进行通讯。有两种方式可以和 Sentinel 进行通讯:
1)是通过直接发送命令来查询被监视 Redis 服务器的当前状态,以及 Sentinel 所知道的关于其他 Sentinel 的信息,诸如此类。
2)是使用发布与订阅功能,通过接收 Sentinel 发送的通知: 当执行故障转移操作,或者某个被监视的服务器被判断为主观下线或者客观下线时, Sentinel 就会发送相应的信息。
哨兵模式工作原理
首先要能理解 SDOWN 和 ODOWN 这两个词的含义,上面已经详细介绍了它们俩。在此再提一下:
SDOWN:subjectively down,直接翻译的为”主观”失效,即当前 sentinel 实例认为某个 redis 服务为”不可用”状态.
ODOWN:objectively down,直接翻译为”客观”失效,即多个 sentinel 实例都认为 master 处于”SDOWN”状态,那么此时 master 将处于 ODOWN,ODOWN 可以简单理解为 master 已经被集群确定
为”不可用”,将会开启 failover.
SDOWN 适合于 master 和 slave,但是 ODOWN 只会使用于 master;当 slave 失效超过”down-after-milliseconds”后,那么所有 sentinel 实例都会将其标记为”SDOWN”。
- SDOWN 与 ODOWN 转换过程:
每个 sentinel 实例在启动后,都会和已知的 slaves/master 以及其他 sentinels 建立 TCP 连接,并周期性发送 PING(默认为 1 秒)
在交互中,如果 redis-server 无法在”down-after-milliseconds”时间内响应或者响应错误信息,都会被认为此 redis-server 处于 SDOWN 状态。
如果 SDOWN 的 server 为 master,那么此时 sentinel 实例将会向其他 sentinel 间歇性(一秒)发送”is-master-down-by-addr“指令并获取响应信息,如果足够多的
sentinel 实例检测到 master 处于 SDOWN,那么此时当前 sentinel 实例标记 master 为 ODOWN…其他 sentinel 实例做同样的交互操作。
配置项”sentinel monitor“,如果检测到 master 处于 SDOWN 状态的 slave 个数达到 ,那么此时此 sentinel 实例将会认为
master 处于 ODOWN。每个 sentinel 实例将会间歇性(10 秒)向 master 和 slaves 发送”INFO”指令,如果 master 失效且没有新 master 选出时,每 1 秒发送一次”INFO”;“INFO”的主要目的就是
获取并确认当前集群环境中 slaves 和 master 的存活情况。
经过上述过程后,所有的 sentinel 对 master 失效达成一致后,开始 failover.
- Sentinel 与 slaves”自动发现”机制:
在 sentinel 的配置文件中(local-sentinel.conf),都指定了 port,此 port 就是 sentinel 实例侦听其他 sentinel 实例建立链接的端口.在集群稳定后,最终会每个 sentinel 实例之间都
会建立一个 tcp 链接,此链接中发送”PING”以及类似于”is-master-down-by-addr”指令集,可用用来检测其他 sentinel 实例的有效性以及”ODOWN”和”failover”过程中信息的交互.
在 sentinel 之间建立连接之前,sentinel 将会尽力和配置文件中指定的 master 建立连接.sentinel 与 master 的连接中的通信主要是基于 pub/sub 来发布和接收信息,发布的信息内容包
括当前 sentinel 实例的侦听端口:
+sentinel sentinel 127.0.0.1:26579 127.0.0.1 26579 …
发布的主题名称为”sentinel:hello”;同时 sentinel 实例也是”订阅”此主题,以获得其他 sentinel 实例的信息.由此可见,环境首次构建时,在默认 master 存活的情况下,所有的
sentinel 实例可以通过 pub/sub 即可获得所有的 sentinel 信息,此后每个 sentinel 实例即可以根据+sentinel 信息中的”ip+port”和其他 sentinel 逐个建立 tcp 连接即可.不过需要提醒
的是,每个 sentinel 实例均会间歇性(5 秒)向”sentinel:hello”主题中发布自己的 ip+port,目的就是让后续加入集群的 sentinel 实例也能或得到自己的信息。
根据上文,我们知道在 master 有效的情况下,即可通过”INFO”指令获得当前 master 中已有的 slave 列表;此后任何 slave 加入集群,master 都会向”主题中”发布”+slave 127.0.0.1:6579 ..“,
那么所有的 sentinel 也将立即获得 slave 信息,并和 slave 建立链接并通过 PING 检测其存活性.
补充一下,每个 sentinel 实例都会保存其他 sentinel 实例的列表以及现存的 master/slaves 列表,各自的列表中不会有重复的信息(不可能出现多个 tcp 连接),对于 sentinel 将使用 ip+port
做唯一性标记,
对于 master/slaver 将使用 runid 做唯一性标记,其中 redis-server 的 runid 在每次启动时都不同.
- Leader 选举:
其实在 sentinels 故障转移中,仍然需要一个”Leader”来调度整个过程:master 的选举以及 slave 的重配置和同步。当集群中有多个 sentinel 实例时,如何选举其中一个 sentinel 为 leader 呢?
在配置文件中”can-failover""quorum”参数,以及”is-master-down-by-addr”指令配合来完成整个过程。
A) “can-failover”用来表明当前 sentinel 是否可以参与”failover”过程,如果为”YES”则表明它将有能力参与”Leader”的选举,否则它将作为”Observer”,observer 参与 leader 选举投票但
不能被选举;
B) “quorum”不仅用来控制 master ODOWN 状态确认,同时还用来选举 leader 时最小”赞同票”数;
C) “is-master-down-by-addr”,它可以用来检测”ip + port”的 master 是否已经处于 SDOWN 状态,不过此指令不仅能够获得 master 是否处于 SDOWN,同时它还额外的返回当前 sentinel
本地”投票选举”的 Leader 信息(runid);
每个 sentinel 实例都持有其他的 sentinels 信息,在 Leader 选举过程中(当为 leader 的 sentinel 实例失效时,有可能 master server 并没失效,注意分开理解),sentinel 实例将从所有的
sentinels 集合中去除”can-failover = no”和状态为 SDOWN 的 sentinels,在剩余的 sentinels 列表中按照 runid 按照”字典”顺序排序后,取出 runid 最小的 sentinel 实例,并将它”投票选举”
为 Leader,并在其他 sentinel 发送的”is-master-down-by-addr”指令时将推选的 runid 追加到响应中。每个 sentinel 实例都会检测”is-master-down-by-addr”的响应结果,如果”投票选举”的
leader 为自己,且状态正常的 sentinels 实例中,“赞同者”的自己的 sentinel 个数不小于(>=) 50% + 1,且不小与
在 sentinel.conf 文件中,我们期望有足够多的 sentinel 实例配置”can-failover yes”,这样能够确保当 leader 失效时,能够选举某个 sentinel 为 leader,以便进行 failover。如果 leader 无法产生,
比如较少的 sentinels 实例有效,那么 failover 过程将无法继续.
- failover 过程:
在 Leader 触发 failover 之前,首先 wait 数秒(随即 0~5),以便让其他 sentinel 实例准备和调整(有可能多个 leader??),如果一切正常,那么 leader 就需要开始将一个 salve 提升为 master,此 slave
必须为状态良好(不能处于 SDOWN/ODOWN 状态)且权重值最低(redis.conf 中)的,当 master 身份被确认后,开始 failover
A)“+failover-triggered”: Leader 开始进行 failover,此后紧跟着”+failover-state-wait-start”,wait 数秒。
B)“+failover-state-select-slave”: Leader 开始查找合适的 slave
C)“+selected-slave”: 已经找到合适的 slave
D) “+failover-state-sen-slaveof-noone”: Leader 向 slave 发送”slaveof no one”指令,此时 slave 已经完成角色转换,此 slave 即为 master
E) “+failover-state-wait-promotition”: 等待其他 sentinel 确认 slave
F)“+promoted-slave”:确认成功
G)“+failover-state-reconf-slaves”: 开始对 slaves 进行 reconfig 操作。
H)“+slave-reconf-sent”:向指定的 slave 发送”slaveof”指令,告知此 slave 跟随新的 master
I)“+slave-reconf-inprog”: 此 slave 正在执行 slaveof + SYNC 过程,如过 slave 收到”+slave-reconf-sent”之后将会执行 slaveof 操作。
J)“+slave-reconf-done”: 此 slave 同步完成,此后 leader 可以继续下一个 slave 的 reconfig 操作。循环 G)
K)“+failover-end”: 故障转移结束
L)“+switch-master”:故障转移成功后,各个 sentinel 实例开始监控新的 master。
哨兵命令
即登录到 sentinel 节点后执行的命令,比如执行” redis-cli -h 192.168.10.203 -p 26379 “命令后,才可以执行下面命令。
PING :返回 PONG 。
SENTINEL masters :列出所有被监视的主服务器,以及这些主服务器的当前状态;
SENTINEL master <master name> 列出一个 master 相关的的信息
SENTINEL slaves <master name> :列出给定主服务器的所有从服务器,以及这些从服务器的当前状态;
SENTINEL sentinels <master name> 列出 master 相关的 sentinels 组其他相关的信息;
SENTINEL get-master-addr-by-name <master name> : 返回给定名字的主服务器的 IP 地址和端口号。如果这个主服务器正在执行故障转移操作,或者针对这个主服务器的故障转移操作已经完成,那么这个命令返回新的主服务器的 IP 地址和端口号;
SENTINEL reset <pattern> : 重置所有名字和给定模式 pattern 相匹配的主服务器。 pattern 参数是一个 Glob 风格的模式。重置操作清楚主服务器目前的所有状态,包括正在执行中的故障转移,并移除目前已经发现和关联的,主服务器的所有从服务器和 Sentinel ;
SENTINEL failover <master name> : 当主服务器失效时,在不询问其他 Sentinel 意见的情况下,强制开始一次自动故障迁移。(不过发起故障转移的 Sentinel 会向其他 Sentinel 发送一个新的配置,其他 Sentinel 会根据这个配置进行相应的更新)
SENTINEL MONITOR <name> <ip> <port> <quorum> 这个命令告诉 sentinel 去监听一个新的 master
SENTINEL REMOVE <name> 命令 sentinel 放弃对某个 master 的监听
SENTINEL SET <name> <option> <value> 这个命令很像 Redis 的 CONFIG SET 命令,用来改变指定 master 的配置。支持多个 <option> <value>。例如以下实例:SENTINEL SET objects-cache-master down-after-milliseconds 1000。只要是配置文件中存在的配置项,都可以用 SENTINEL SET 命令来设置。这个还可以用来设置 master 的属性,比如说 quorum (票数),而不需要先删除 master,再重新添加 master。例如:SENTINEL SET objects-cache-master quorum 5。
其他
增加或删除 Sentinel
由于有 sentinel 自动发现机制,所以添加一个 sentinel 到你的集群中非常容易,你所需要做的只是监控到某个 Master 上,然后新添加的 sentinel 就能获得其他 sentinel 的信息以及 master 所有的 slaves。
如果你需要添加多个 sentinel,建议你一个接着一个添加,这样可以预防网络隔离带来的问题。你可以每个 30 秒添加一个 sentinel。最后你可以用 SENTINEL MASTER mastername 来检查一下是否所有的 sentinel 都已经监控到了 master。
删除一个 sentinel 显得有点复杂:因为 sentinel 永远不会删除一个已经存在过的 sentinel,即使它已经与组织失去联系很久了。
要想删除一个 sentinel,应该遵循如下步骤:
1)停止所要删除的 sentinel
2)发送一个 SENTINEL RESET * 命令给所有其它的 sentinel 实例,如果你想要重置指定 master 上面的 sentinel,只需要把*号改为特定的名字,注意,需要一个接一个发,每次发送的间隔不低于 30 秒。
3)检查一下所有的 sentinels 是否都有一致的当前 sentinel 数。使用 SENTINEL MASTER mastername 来查询。
删除旧 master 或者不可达 slave
sentinel 永远会记录好一个 Master 的 slaves,即使 slave 已经与组织失联好久了。这是很有用的,因为 sentinel 集群必须有能力把一个恢复可用的 slave 进行重新配置。
并且,failover 后,失效的 master 将会被标记为新 master 的一个 slave,这样的话,当它变得可用时,就会从新 master 上复制数据。
然后,有时候你想要永久地删除掉一个 slave(有可能它曾经是个 master),你只需要发送一个 SENTINEL RESET master 命令给所有的 sentinels,它们将会更新列表里能够正确地复制 master 数据的 slave。
发布与订阅信息(sentinel 的日志文件里可以看到这些信息)
客户端可以将 Sentinel 看作是一个只提供了订阅功能的 Redis 服务器: 你不可以使用 PUBLISH 命令向这个服务器发送信息,但你可以用 SUBSCRIBE 命令或者 PSUBSCRIBE 命令,通过订阅给定的频道来获取相应的事件提醒。
一个频道能够接收和这个频道的名字相同的事件。比如说,名为 +sdown 的频道就可以接收所有实例进入主观下线(SDOWN)状态的事件。
通过执行 “PSUBSCRIBE * “命令可以接收所有事件信息(即订阅所有消息)。
以下列出的是客户端可以通过订阅来获得的频道和信息的格式: 第一个英文单词是频道/事件的名字,其余的是数据的格式。
注意,当格式中包含 instance details 字样时,表示频道所返回的信息中包含了以下用于识别目标实例的内容.
以下是所有可以收到的消息的消息格式,如果你订阅了所有消息的话。第一个单词是频道的名字,其它是数据的格式。
注意:以下的 instance details 的格式是:
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
如果这个 redis 实例是一个 master,那么@之后的消息就不会显示。
可以看出,使用 Sentinel 命令和发布订阅两种机制就能很好的实现和客户端的集成整合:
使用 get-master-addr-by-name 和 slaves 指令可以获取当前的 Master 和 Slaves 的地址和信息;而当发生故障转移时,即 Master 发生切换,可以通过订阅的+switch-master 事件获得最新的 Master 信息。
sentinel.conf 中的 notification-script
在 sentinel. conf 中可以配置多个 sentinel notification-script <master name> <shell script-path>, 如 sentinel notification-script mymaster ./check. sh
这个是在群集 failover 时会触发执行指定的脚本。脚本的执行结果若为 1,即稍后重试(最大重试次数为 10);若为 2,则执行结束。并且脚本最大执行时间为 60 秒,超时会被终止执行。
目前会存在该脚本被执行多次的问题,网上查找资料获得的解释是:脚本分为两个级别, SENTINEL_LEADER 和 SENTINEL_OBSERVER ,前者仅由领头 Sentinel 执行(一个 Sentinel),而后者由监视同一个 master 的所有 Sentinel 执行(多个 Sentinel)。
无 failover 时的配置纠正
即使当前没有 failover 正在进行,sentinel 依然会使用当前配置去设置监控的 master。特别是:
1)根据最新配置确认为 slaves 的节点却声称自己是 master(上文例子中被网络隔离后的的 redis3),这时它们会被重新配置为当前 master 的 slave。
2)如果 slaves 连接了一个错误的 master,将会被改正过来,连接到正确的 master。
Slave 选举与优先级
当一个 sentinel 准备好了要进行 failover,并且收到了其他 sentinel 的授权,那么就需要选举出一个合适的 slave 来做为新的 master。
slave 的选举主要会评估 slave 的以下几个方面:
1)与 master 断开连接的次数
2)Slave 的优先级
3)数据复制的下标(用来评估 slave 当前拥有多少 master 的数据)
4)进程 ID
如果一个 slave 与 master 失去联系超过 10 次,并且每次都超过了配置的最大失联时间(down-after-milliseconds),如果 sentinel 在进行 failover 时发现 slave 失联,那么这个 slave 就会被 sentinel 认为不适合用来做新 master 的。
更严格的定义是,如果一个 slave 持续断开连接的时间超过
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
就会被认为失去选举资格。
符合上述条件的 slave 才会被列入 master 候选人列表,并根据以下顺序来进行排序:
1)sentinel 首先会根据 slaves 的优先级来进行排序,优先级越小排名越靠前。
2)如果优先级相同,则查看复制的下标,哪个从 master 接收的复制数据多,哪个就靠前。
3)如果优先级和下标都相同,就选择进程 ID 较小的那个。
一个 redis 无论是 master 还是 slave,都必须在配置中指定一个 slave 优先级。要注意到 master 也是有可能通过 failover 变成 slave 的。
如果一个 redis 的 slave 优先级配置为 0,那么它将永远不会被选为 master。但是它依然会从 master 哪里复制数据。
配置文件
1)sentinel monitor mymaster 192.168.10.202 6379 2
Sentine 监听的 maste 地址,第一个参数是给 master 起的名字,第二个参数为 master IP,第三个为 master 端口,第四个为当该 master 挂了的时候,若想将该 master 判为失效,
在 Sentine 集群中必须至少 2 个 Sentine 同意才行,只要该数量不达标,则就不会发生故障迁移。也就是说只要有 2 个 sentinel 认为 master 下线,就认为该 master 客观下线,
启动 failover 并选举产生新的 master。通常最后一个参数不能多于启动的 sentinel 实例数。
这个配置是sentinel需要监控的master/slaver信息,格式为sentinel monitor <mastername> <masterIP> <masterPort> <quorum>
其中<quorum>应该小于集群中slave的个数,当失效的节点数超过了<quorum>,则认为整个体系结构失效
不过要注意,无论你设置要多少个 Sentinel 同意才能判断一个服务器失效,一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持,才能发起一次自动故障迁移,
并预留一个给定的配置纪元 (configuration Epoch ,一个配置纪元就是一个新主服务器配置的版本号)。
换句话说,在只有少数(minority) Sentinel 进程正常运作的情况下, Sentinel 是不能执行自动故障迁移的。
-----------------------------------------------------------------------------------------------
2)sentinel down-after-milliseconds mymaster 30000
表示 master 被当前 sentinel 实例认定为失效的间隔时间。
master 在多长时间内一直没有给 Sentine 返回有效信息,则认定该 master 主观下线。也就是说如果多久没联系上 redis-servevr,认为这个 redis-server 进入到失效(SDOWN)状态。
如果服务器在给定的毫秒数之内,没有返回 Sentinel 发送的 PING 命令的回复,或者返回一个错误,那么 Sentinel 将这个服务器标记为主观下线(subjectively down,简称 SDOWN )。
不过只有一个 Sentinel 将服务器标记为主观下线并不一定会引起服务器的自动故障迁移: 只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后,服务器才会被标记为客观下线
(objectively down,简称 ODOWN ),这时自动故障迁移才会执行。
将服务器标记为客观下线所需的 Sentinel 数量由对主服务器的配置决定。
-----------------------------------------------------------------------------------------------
3)sentinel parallel-syncs mymaster 2
当在执行故障转移时,设置几个 slave 同时进行切换 master,该值越大,则可能就有越多的 slave 在切换 master 时不可用,可以将该值设置为 1,即一个一个来,这样在某个
slave 进行切换 master 同步数据时,其余的 slave 还能正常工作,以此保证每次只有一个从服务器处于不能处理命令请求的状态。
parallel-syncs 选项指定了在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小,完成故障转移所需的时间就越长。
如果从服务器被设置为允许使用过期数据集(参见对 redis.conf 文件中对 slave-serve-stale-data 选项的说明),那么你可能不希望所有从服务器都在同一时间向新的主服务器发送同步请求,
因为尽管复制过程的绝大部分步骤都不会阻塞从服务器,但从服务器在载入主服务器发来的 RDB 文件时,仍然会造成从服务器在一段时间内不能处理命令请求: 如果全部从服务器一起对新的主
服务器进行同步,那么就可能会造成所有从服务器在短时间内全部不可用的情况出现。
当新 master 产生时,同时进行"slaveof"到新 master 并进行"SYNC"的 slave 个数。
默认为 1,建议保持默认值
在 salve 执行 salveof 与同步时,将会终止客户端请求。
此值较大,意味着"集群"终止客户端请求的时间总和和较大。
此值较小,意味着"集群"在故障转移期间,多个 salve 向客户端提供服务时仍然使用旧数据。
-----------------------------------------------------------------------------------------------
4)sentinel can-failover mymaster yes
在 sentinel 检测到 O_DOWN 后,是否对这台 redis 启动 failover 机制
-----------------------------------------------------------------------------------------------
5)sentinel auth-pass mymaster 20180408
设置 sentinel 连接的 master 和 slave 的密码,这个需要和 redis.conf 文件中设置的密码一样
-----------------------------------------------------------------------------------------------
6)sentinel failover-timeout mymaster 180000
failover 过期时间,当 failover 开始后,在此时间内仍然没有触发任何 failover 操作,当前 sentinel 将会认为此次 failoer 失败。
执行故障迁移超时时间,即在指定时间内没有大多数的 sentinel 反馈 master 下线,该故障迁移计划则失效
-----------------------------------------------------------------------------------------------
7)sentinel config-epoch mymaster 0
选项指定了在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步。这个数字越小,完成故障转移所需的时间就越长。
-----------------------------------------------------------------------------------------------
8)sentinel notification-script mymaster /var/redis/notify.sh
当 failover 时,可以指定一个"通知"脚本用来告知当前集群的情况。
脚本被允许执行的最大时间为 60 秒,如果超时,脚本将会被终止(KILL)
-----------------------------------------------------------------------------------------------
9)sentinel leader-epoch mymaster 0
同时一时间最多 0 个 slave 可同时更新配置,建议数字不要太大,以免影响正常对外提供服务。