Redis Sentinel看完这篇就够了

Redis Sentinel看完这篇就够了_第1张图片

摘要

现阶段的Redis集群主要有两种,一种是高可用集群,Redis Sentinel,一主多从架构,每个实例都持有完整的数据。另一种是分布式集群,Redis Cluster,多主架构,数据分布在各个实例之间,每个实例都负责数据的读写。今天我们来看看Redis Sentinel的理论与实践。

Sentinel

功能

Redis 的 Sentinel 系统用于管理多个 Redis 服务器, 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

性质

Sentinel是一个分布式系统,Sentinel本身设计为在有多个Sentinel进程协同合作的配置中运行。优点如下:

  • 多个Sentinel投票决定Master的自动故障转移,降低误判率;
  • 如果故障转移系统是单点的,那个整个系统无法达到高可用。

部署前需要了解的基本知识

运行Sentinel时必须使用配置文件,否则Sentinel只会拒绝启动。

Sentinels默认情况下会监听TCP端口26379的连接,因此,要使Sentinels正常工作,服务器的端口26379 必须打开才能从其他Sentinel实例的IP地址接收连接。!!!这一点十分重要!!!

  • 一个健壮的系统至少需要三个Sentinel实例。

  • 应将三个Sentinel实例放置到独立的计算机或虚拟机中。

  • 因为Redis使用异步复制,所以Sentinel + Redis高可用方案。有关异步复制的内容可以参考《浅析Redis复制过程》。

  • 客户端必须支持Sentinel。

环境

  • CentOS 7
  • Redis4.0.14
  • 虚拟机

部署架构图

Redis Sentinel看完这篇就够了_第2张图片

部署过程

整体部署过程为先部署上图下半部分的主从集群,再部署sentinel集群。

部署主从集群

分别在Master、Slave1、Slave2下载redis,存放路径 /usr/redis/

[root@localhost ~]wget -P /usr/redis/ http://download.redis.io/releases/redis-4.0.14.tar.gz

分别解压下载好的文件,并安装

[root@localhost ~]cd /usr/redis/
[root@localhost redis]gunzip redis-4.0.14.tar.gz
[root@localhost redis]tar -xvf redis-4.0.14.tar
[root@localhost redis]cd redis-4.0.14
[root@localhost redis-4.0.14]make install

配置Slave1、Slave2的redis.conf文件,让Slave1、Slave2能从Master中复制数据,复制的原理可参考浅析Redis复制过程。下面给出主从集群所需的最少配置:

# slaveof  
slaveof 192.168.33.160 6379

# masterauth 
masterauth 123456

#默认,推荐设置,slave设置只读
slave-read-only yes

#bind 127.0.0.1 允许其他机器访问
bind 0.0.0.0

#daemonize no 后台运行
daemonize yes

分别启动Master,Slave1,Slave2

[root@localhost redis-4.0.14]cd /src
[root@localhost src]./redis-server ../redis.conf

连上Master,并在上面set一些值,看看同步情况

[root@localhost src]./redis-cli
127.0.0.1:6379>set key1 value1
"ok"
127.0.0.1:6379>quit

连上Slave1,Slave2,尝试着获取key1的值

[root@localhost src]./redis-cli
127.0.0.1:6379>get key1
"value1"
127.0.0.1:6379>quit

至此,主从同步已经部署完成,就下来我们来部署sentinel集群。

部署sentinel集群

Sentinel安装在和Redis同一台机器上,对应的关系如下表:

Host Redis Sentinel
[Host1]192.168.33.4 Master Sentinel1
[Host2]192.168.33.5 Slave1 Sentinel2
[Host3]192.168.33.6 Slave2 Sentinel3

由于Sentinel已经包含在Redis 2.8.0以上的版本中,所以我们可以直接使用之前下载好的redis-4.0.14部署。

分别在Master、Slave1、Slave2对应机器上配置sentinel.conf,下面给出sentinel集群所需的最少配置:

sentinel monitor mymaster 192.168.33.4 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
#后台启动
daemonize yes
#记录日志
logfile "/usr/redis/redis-4.0.14/sentinel_log.log"
#允许其他机器访问,特别要保证sentinel集群之间的机器能够相互访问
bind 0.0.0.0
#关闭保护模块,方便测试
protected-mode no

说明:

  • monitor:配置指示 Sentinel 去监视一个名为 mymaster 的主服务器 IP 地址为 192.168.33.4 端口号为 6379 主服务器判断为失效至少需要 2 个 Sentinel 同意(无论你设置要多少个 Sentinel 同意才能判断一个服务器失效, 一个 Sentinel 都需要获得系统中多数Sentinel 的支持, 才能发起一次自动故障迁移)。
  • down-after-milliseconds:Sentinel 认为服务器已经断线所需的毫秒数。
  • parallel-syncs:选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长。

分别启动Master,Slave1,Slave2上的sentinel

[root@localhost src]./redis-server ../sentinel.conf --sentinel

至此,sentinel集群已搭建完毕。

测试

测试一下sentinel集群的故障转移能力:

  1. 关闭Master进程,其中7081为Master的进程号:

    [root@localhost ~]ps -ef | grep redis
    [root@localhost ~]kill -s 9 7081
    
  2. 观察sentinel的日志:

    Redis Sentinel看完这篇就够了_第3张图片
    说明:

    • +sdowm master ,表示Master主观下线;
    • +odown master,表示Master客观下线;
    • +switch-master ,表示重现选举master;
  3. 现在192.168.33.6的redis被选举为Master,即Slave2,现在我们尝试在新的Masert(Slave2)上操作,看看能否同步到Slave1上:

    #进入新的Masert(Slave2)
    [[email protected] src]./redis-cli
    127.0.0.1:6379>set key2 value2
    "ok"
    
  4. 我们登录到192.168.33.5的redis(Slave1)中,看看是否能够取到key2的值:

    #进入Slave1
    [[email protected] src]./redis-cli
    127.0.0.1:6379>get key2
    "value2"
    

    我们可以清楚到看到,能够在Slave1中取到新的Masert(Slave2)新插入的值,所以sentinel成功的完成了一次故障转移。

主观下线和客观下线

前面有提到过,关于主观下线和客观下线的概念,这里详细说明一下:

  • 主观下线:(SDOWN):指的是单个Sentinel对服务器做出的下线判断;
  • 客观下线:(ODOWN):指的是多个Sentinel对服务器做出SDOWN判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。(一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)

那么Sentinel是如何认定服务器下线的呢?(说明一下,上面提到的服务器包括Master,Slave,Sentinel)

如果一个服务器没有在 master-down-after-milliseconds指定的时间内,对向它发送PING命令的Sentinel返回一个有效的相应,那么Sentinel就将该服务器标记为主观下线。有效的相应包括下面这些:

  • +PONG
  • -LOADING
  • -MASTERDOWN

注意, 一个服务器必须在 master-down-after-milliseconds 毫秒内, 一直返回无效回复才会被 Sentinel 标记为主观下线。

举个例子,如果 master-down-after-milliseconds 选项的值为 30000 毫秒(30 秒), 那么只要服务器能在每 29 秒之内返回至少一次有效回复, 这个服务器就仍然会被认为是处于正常状态的。

从主观下线状态切换到客观下线状态并没有使用严格的法定人数算法,而是使用了流言协议:

如果 Sentinel 在给定的时间范围内, 从其他 Sentinel 那里接收到了足够数量的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线。 如果之后其他 Sentinel 不再报告主服务器已下线, 那么客观下线状态就会被移除。

客观下线条件只适用于Master,对于任何其他类型的 Redis 实例(Sentinel,Slave),Sentinel 在将它们判断为下线前不需要进行协商,所以Slave或者其他 Sentinel 永远不会达到客观下线条件。

每个Sentinel定期需要执行的任务

  • 每个 Sentinel 以每秒钟一次的频率向Master、Slave以及其他 Sentinel 实例发送一个 PING 命令。
  • 如果一个服务器(Master,Slave,Sentinel)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。
  • 如果一个Master被标记为主观下线, 那么正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master是否真的进入了主观下线状态。
  • 如果一个Master被标记为主观下线, 并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个Master被标记为客观下线
  • 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master和Slave发送 INFO 命令。 当一个主服务器被 Sentinel 标记为客观下线时, Sentinel 向下线Master的所有Slave发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
  • 当没有足够数量的 Sentinel 同意Master已经下线, Master的客观下线状态就会被移除。 当Master重新向 Sentinel 的 PING 命令返回有效回复时, 主服务器的主观下线状态就会被移除。

自动发现 Sentinel 和从服务器

一个 Sentinel 可以与其他多个 Sentinel 进行连接, 各个 Sentinel 之间可以互相检查对方的可用性, 并进行信息交换。无须为运行的每个 Sentinel 分别设置其他 Sentinel 的地址,因为 Sentinel 可以通过 Pub/Sub(发布/订阅) 功能来自动发现正在监视相同Master的其他 Sentinel , 这一功能是通过向频道 sentinel:hello 发送信息来实现的。

同样的,你也不必手动列出 Master下的所有Slave, 因为 Sentinel 可以通过询问主服务器来获得所有Slave的信息。

  • 每个 Sentinel 会以每两秒一次的频率, 通过发布/订阅功能, 向被它监视的所有Master和Slave的 sentinel:hello 频道发送一条信息, 信息中包含了 Sentinel 的 IP 地址、端口号和运行 ID (runid)。
  • 每个 Sentinel 都订阅了被它监视的 Master和Slave的 sentinel:hello 频道, 查找之前未出现过的 sentinel。 当一个 Sentinel 发现一个新的 Sentinel 时, 它会将新的 Sentinel 添加到 Sentinel 列表中, 这个列表保存了当前Sentinel已知的、监视同一个主服务器的所有其他 Sentinel 。
  • Sentinel 发送的信息中还包括完整的Master当前配置(configuration)。 如果一个 Sentinel 包含的Master配置比另一个 Sentinel 发送的配置要旧, 那么这个 Sentinel 会立即升级到新配置上。(故障转移,发现新的Slave等等)

发布与订阅信息

客户端可以将 Sentinel 看作是一个只提供了订阅功能的 Redis 服务器: 可以通过订阅给定的频道来获取相应的事件提醒。

比如,名为 +sdown 的频道就可以接收所有实例进入主观下线(SDOWN)状态的事件。Sentinel通过订阅Sentinel列表中的其他Sentinel的+sdown频道,接受所有进入主观下线的服务器事件,对于Master来说,进一步确定是否需要进入客观下线。

故障转移

  • 发现Master已经进入客观下线状态。
  • 对我们的当前纪元进行自增, 并尝试在这个纪元中当选。
  • 如果当选失败, 那么在设定的故障迁移超时时间的两倍之后, 重新尝试当选。 如果当选成功, 那么执行以下步骤。
  • 选出一个从服务器,并将它升级为主服务器。
  • 向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为主服务器。
  • 通过发布与订阅功能, 将更新后的配置传播给所有其他 Sentinel , 其他 Sentinel 对它们自己的配置进行更新。
  • 向已下线Master的Slave发送 SLAVEOF 命令, 让它们去复制新的主服务器。
  • 当所有从服务器都已经开始复制新的主服务器时, 领头 Sentinel 终止这次故障迁移操作。

Sentinel 选择新的Master的规则:

  • 在失效Master属下的Slave当中, 那些被标记为主观下线、已断线、或者最后一次回复 PING 命令的时间大于五秒钟的从服务器都会被淘汰。
  • 在失效Master属下的Slave当中, 那些与失效Master连接断开的时长超过 down-after 选项指定的时长十倍的从服务器都会被淘汰。
  • 在经历了以上两轮淘汰之后剩下来的Slave中, 我们选出复制偏移量(replication offset)最大的那个从服务器作为新的Master; 如果复制偏移量不可用, 或者从服务器的复制偏移量相同, 那么带有最小运行 ID 的那个从服务器成为新的主服务器。(至于为什么选择runid小的?你可以理解为runid小意味着先启动,年龄长,年长者总是容易被委以重任)

TILT 模式

Redis Sentinel 严重依赖计算机的时间功能: 比如说, 为了判断一个实例是否可用, Sentinel 会记录这个实例最后一次相应 PING 命令的时间, 并将这个时间和当前时间进行对比, 从而知道这个实例有多长时间没有和 Sentinel 进行任何成功通讯。

不过, 一旦计算机的时间功能出现故障, 或者计算机非常忙碌, 又或者进程因为某些原因而被阻塞时, Sentinel 可能也会跟着出现故障。

TILT 模式是一种特殊的保护模式: 当 Sentinel 发现系统有些不对劲时, Sentinel 就会进入 TILT 模式。

那它是如何发现不对劲的呢?

Sentinel 的时间中断器默认每秒执行 10 次, 所以我们预期时间中断器的两次执行之间的间隔为 100 毫秒左右。

基于这个特性,Sentinel 的做法是, 记录上一次时间中断器执行时的时间, 并将它和这一次时间中断器执行的时间进行对比:

  • 如果两次调用时间之间的差距为负值, 或者非常大(超过 2 秒钟), 那么 Sentinel 进入 TILT 模式。
  • 如果 Sentinel 已经进入 TILT 模式, 那么 Sentinel 延迟退出 TILT 模式的时间。

当 Sentinel 进入 TILT 模式时, 它仍然会继续监视所有目标, 但是:

  • 它不再执行任何操作,比如故障转移。
  • 当有实例向这个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令时, Sentinel 返回负值: 因为这个 Sentinel 所进行的下线判断已经不再准确。

如果 TILT 可以正常维持 30 秒钟, 那么 Sentinel 退出 TILT 模式。

参考

[1] Redis Sentinel Documentation.

你可能感兴趣的:(Redis)