文章目录
简介
Redis Sentinel可以配合Redis的复制功能使用,并对下线的主服务器进行故障转移。
Redis Sentinel是运行在特殊模式下的Redis服务器,但它的行为和一般的 Redis 服务器并不相同。
Sentinel会监视一系列主服务器以及这些主服务器的从服务器,通过向主服务器发送PUBLISH命令和SUBSCRIBE命令,并向主服务器和从服务器发送PING命令,各个Sentinel进程可以自主识别可用的从服务器和其他Sentinel。
当主服务器失效的时候,监视这个主服务器的所有sentinel就会基于彼此共有的信息选出一个sentinel,并从现有的从服务器当中选出一个新的主服务器。
当被选中的从服务器转换成主服务器之后,那个被选中的Sentinel就会让剩余的其他从服务器去复制这个新的主服务器(在默认设置下,Sentinel会一个接一个地迁移从服务器,但这个数量可以通过配置选项进行修改)。
架构
它由两部分组成,哨兵节点和数据节点:
哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的redis节点,不存储数据。
数据节点:主节点和从节点都是数据节点。
哨兵模式的作用
- 监控( Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒( Notification):当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 API 向管理员
或者其他应用程序发送通知。 - 自动故障迁移( Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动
故障迁移操作,它会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的
其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回
新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。
启动哨兵模式
对于 redis-sentinel 程序,你可以用以下命令来启动 Sentinel 系统:
redis-sentinel /path/to/sentinel.conf
对于 redis-server程序,你可以用以下命令来启动一个运行在 Sentinel 模式下的 Redis 服务器:
redis-server /path/to/sentinel.conf --sentinel
两种方法都可以启动一个 Sentinel 实例。
启动 Sentinel 实例必须指定相应的配置文件,系统会使用配置文件来保存 Sentinel 的当前状态,并在Sentinel 重启时通过载入配置文件来进行状态还原。
如果启动 Sentinel时没有指定相应的配置文件,或者指定的配置文件不可写( not writable),那么Sentinel会拒绝启动。
配置哨兵模式
Redis 源码中包含了一个名为 sentinel.conf 的文件,这个文件是一个带有详细注释的 Sentinel 配置文件
示例。
运行一个 Sentinel 所需的最少配置如下所示:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
第一行配置指示 Sentinel 去监视一个名为 mymaster 的主服务器,这个主服务器的 IP 地址为 127.0.0.1 ,
端口号为 6379 ,而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意(只要同意 Sentinel 的数量不达
标,自动故障迁移就不会执行)。
不过要注意,无论你设置要多少个 Sentinel 同意才能判断一个服务器失效,一个 Sentinel 都需要获得
系统中多数Sentinel 的支持,才能发起一次自动故障迁移, 并预留一个给定的配置纪元,一个配置纪元就是一个新主服务器配置的版本号)。
换句话说,在只有少数Sentinel进程正常运作的情况下,Sentinel 是不能执行自动故障迁移的.
- down-after-milliseconds
选项指定了 Sentinel认为服务器已经断线所需的毫秒数。如果服务器在给定的毫秒数之内,没有返回 Sentinel 发送的PING 命令的回复,或者返回一个错误,那么 Sentinel 将这个服务器标记为主观下线。
不过只有一个 Sentinel将服务器标记为主观下线并不一定会引起服务器的自动故障迁移:只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后,服务器才会被标记为客观下线,这时自动故障迁移才会执行。将服务器标记为客观下线所需的 Sentinel 数量由对主服务器的配置决定.
- parallel-syncs
选项指定了在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小,完成故障转移所需的时间就越长
如果从服务器被设置为允许使用过期数据集,那么你可能不希望所有从服务器都在同一时间向新的主服务器发送同步请求,因为尽管复制过程的绝大部分步骤都不会阻塞从服务器,但从服务器在载入主服务器发来的RDB文件时,仍然会造成从服务器在一段时间内不能处理命令请求:如果全部从服务器一起对新的主服务器进行同步,那么就可能会造成所有从服务器在短时间内全部不可用的情况出现。
你可以通过将这个值设为1来保证每次只有一个从服务器处于不能处理命令请求的状态。
主观下线和客观下线
- 主观下线( Subjectively Down,简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。
- 客观下线( Objectively Down,简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出
SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的服务器下
线判断。(一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令
来询问对方是否认为给定的服务器已下线。)
如果一个服务器没有在 master-down-after-milliseconds 选项所指定的时间内,对向它发送PING 命令
的 Sentinel 返回一个有效回复( valid reply),那么 Sentinel 就会将这个服务器标记为主观下线。
服务器对PING 命令的有效回复可以是以下三种回复的其中一种:
- 返回 +PONG 。
- 返回 -LOADING 错误。
- 返回 -MASTERDOWN 错误
如果服务器返回除以上三种回复之外的其他回复,又或者在指定时间内没有回复PING 命令,那么 Sentinel
认为服务器返回的回复无效( non-valid)
注意,一个服务器必须在 master-down-after-milliseconds 毫秒内,一直返回无效回复才会被 Sentinel
标记为主观下线。
举个例子,如果 master-down-after-milliseconds 选项的值为 30000 毫秒( 30 秒),那么只要服务器能
在每 29 秒之内返回至少一次有效回复,这个服务器就仍然会被认为是处于正常状态的。
从主观下线状态切换到客观下线状态并没有使用严格的法定人数算法( strong quorum algorithm),而是使
用了流言协议:如果 Sentinel 在给定的时间范围内,从其他 Sentinel 那里接收到了足够数量的主服务器下
线报告,那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线。如果之后其他 Sentinel 不再报
告主服务器已下线,那么客观下线状态就会被移除。
客观下线条件只适用于主服务器:对于任何其他类型的 Redis 实例,Sentinel 在将它们判断为下线前不需要
进行协商,所以从服务器或者其他 Sentinel 永远不会达到客观下线条件。
只要一个 Sentinel 发现某个主服务器进入了客观下线状态,这个 Sentinel 就可能会被其他 Sentinel 推选出,
并对失效的主服务器执行自动故障迁移操作。
每个 Sentinel 都需要定期执行的任务
- 每个 Sentinel以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个PING 命令。
- 如果一个实例( instance)距离最后一次有效回复PING 命令的时间超过down-after-milliseconds选项所指定的值,那么这个实例会被Sentinel标记为主观下线。一个有效回复可以是:+PONG 、 -LOADING或者 -MASTERDOWN 。
- 如果一个主服务器被标记为主观下线,那么正在监视这个主服务器的所有 Sentinel要以每秒一次的频率确认主服务器的确进入了主观下线状态。
- 如果一个主服务器被标记为主观下线,并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)
在指定的时间范围内同意这一判断,那么这个主服务器被标记为客观下线。 - 在一般情况下,每个Sentinel会以每10秒一次的频率向它已知的所有主服务器和从服务器发送INFO命令。当一个主服务器被 Sentinel 标记为客观下线时,Sentinel向下线主服务器的所有从服务器发送INFO 命令的频率会从 10 秒一次改为每秒一次。
- 当没有足够数量的Sentinel同意主服务器已经下线,主服务器的客观下线状态就会被移除。当主服务器重新向Sentinel的PING命令返回有效回复时,主服务器的主管下线状态就会被移除。
自动发现 Sentinel 和从服务器
一个 Sentinel 可以与其他多个 Sentinel 进行连接,各个 Sentinel 之间可以互相检查对方的可用性,并进行
信息交换。
你无须为运行的每个 Sentinel 分别设置其他 Sentinel 的地址,因为 Sentinel 可以通过发布与订阅功能来自
动发现正在监视相同主服务器的其他 Sentinel ,这一功能是通过向频道 sentinel:hello 发送信息来
实现的。
与此类似,你也不必手动列出主服务器属下的所有从服务器,因为 Sentinel 可以通过询问主服务器来获得所
有从服务器的信息。
- 每个 Sentinel 会以每两秒一次的频率,通过发布与订阅功能,向被它监视的所有主服务器和从服务器
的 sentinel:hello 频道发送一条信息,信息中包含了 Sentinel 的 IP 地址、端口号和运行 ID
( runid)。 - 每个 Sentinel 都订阅了被它监视的所有主服务器和从服务器的 sentinel:hello 频道,查找之前
未出现过的 sentinel ( looking for unknown sentinels)。当一个 Sentinel 发现一个新的 Sentinel 时,它
会将新的 Sentinel 添加到一个列表中,这个列表保存了 Sentinel 已知的,监视同一个主服务器的所有
其他 Sentinel 。 - Sentinel 发送的信息中还包括完整的主服务器当前配置( configuration)。如果一个 Sentinel 包含的主
服务器配置比另一个 Sentinel 发送的配置要旧,那么这个 Sentinel 会立即升级到新配置上。 - 在将一个新 Sentinel 添加到监视主服务器的列表上面之前,Sentinel 会先检查列表中是否已经包含了
和要添加的 Sentinel 拥有相同运行 ID 或者相同地址(包括 IP 地址和端口号)的 Sentinel ,如果是的
话,Sentinel 会先移除列表中已有的那些拥有相同运行 ID 或者相同地址的 Sentinel ,然后再添加新
Sentinel 。
故障转移
一次故障转移操作由以下步骤组成:
- 发现主服务器已经进入客观下线状态。
- 对我们的当前纪元进行自增,并尝试在这个纪元中当选。
- 如果当选失败,那么在设定的故障迁移超时时间的两倍之后,重新尝试当选。如果当选成功,那么执行以下步骤。
- 选出一个从服务器,并将它升级为主服务器。
- 向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为主服务器。
- 通过发布与订阅功能,将更新后的配置传播给所有其他 Sentinel ,其他 Sentinel 对它们自己的配置进行更新。
- 向已下线主服务器的从服务器发送SLAVEOF 命令,让它们去复制新的主服务器。
- 当所有从服务器都已经开始复制新的主服务器时,领头 Sentinel 终止这次故障迁移操作。
Sentinel 使用以下规则来选择新的主服务器:
- 在失效主服务器属下的从服务器当中,那些被标记为主观下线、已断线、或者最后一次回复PING 命
令的时间大于五秒钟的从服务器都会被淘汰。 - 在失效主服务器属下的从服务器当中,那些与失效主服务器连接断开的时长超过 down-after 选项指
定的时长十倍的从服务器都会被淘汰。 - 在经历了以上两轮淘汰之后剩下来的从服务器中,我们选出复制偏移量( replication offset)最大的那
个从服务器作为新的主服务器;如果复制偏移量不可用,或者从服务器的复制偏移量相同,那么带有
最小运行 ID 的那个从服务器成为新的主服务器。
Sentinel 自动故障迁移的一致性特质
Sentinel 自动故障迁移使用 Raft 算法来选举领头( leader) Sentinel ,从而确保在一个给定的纪元( epoch)
里,只有一个领头产生。
这表示在同一个纪元中,不会有两个 Sentinel 同时被选中为领头,并且各个 Sentinel 在同一个纪元中只会
对一个领头进行投票。
更高的配置纪元总是优于较低的纪元,因此每个 Sentinel 都会主动使用更新的纪元来代替自己的配置。
简单来说,我们可以将 Sentinel 配置看作是一个带有版本号的状态。一个状态会以最后写入者胜出( lastwrite-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发来的配置更新,从而避免自身因为保存了过期的配置而对实例进行了不必要的
重新配置。