现阶段的Redis集群主要有两种,一种是高可用集群,Redis Sentinel,一主多从架构,每个实例都持有完整的数据。另一种是分布式集群,Redis Cluster,多主架构,数据分布在各个实例之间,每个实例都负责数据的读写。今天我们来看看Redis Sentinel的理论与实践。
Redis 的 Sentinel 系统用于管理多个 Redis 服务器, 该系统执行以下三个任务:
Sentinel是一个分布式系统,Sentinel本身设计为在有多个Sentinel进程协同合作的配置中运行。优点如下:
运行Sentinel时必须使用配置文件,否则Sentinel只会拒绝启动。
Sentinels默认情况下会监听TCP端口26379的连接,因此,要使Sentinels正常工作,服务器的端口26379 必须打开才能从其他Sentinel实例的IP地址接收连接。!!!这一点十分重要!!!
一个健壮的系统至少需要三个Sentinel实例。
应将三个Sentinel实例放置到独立的计算机或虚拟机中。
因为Redis使用异步复制,所以Sentinel + Redis高可用方案。有关异步复制的内容可以参考《浅析Redis复制过程》。
客户端必须支持Sentinel。
整体部署过程为先部署上图下半部分的主从集群,再部署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安装在和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
说明:
分别启动Master,Slave1,Slave2上的sentinel
[root@localhost src]./redis-server ../sentinel.conf --sentinel
至此,sentinel集群已搭建完毕。
测试一下sentinel集群的故障转移能力:
关闭Master进程,其中7081为Master的进程号:
[root@localhost ~]ps -ef | grep redis
[root@localhost ~]kill -s 9 7081
观察sentinel的日志:
现在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"
我们登录到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成功的完成了一次故障转移。
前面有提到过,关于主观下线和客观下线的概念,这里详细说明一下:
那么Sentinel是如何认定服务器下线的呢?(说明一下,上面提到的服务器包括Master,Slave,Sentinel)
如果一个服务器没有在 master-down-after-milliseconds指定的时间内,对向它发送PING命令的Sentinel返回一个有效的相应,那么Sentinel就将该服务器标记为主观下线。有效的相应包括下面这些:
注意, 一个服务器必须在 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 进行连接, 各个 Sentinel 之间可以互相检查对方的可用性, 并进行信息交换。无须为运行的每个 Sentinel 分别设置其他 Sentinel 的地址,因为 Sentinel 可以通过 Pub/Sub(发布/订阅) 功能来自动发现正在监视相同Master的其他 Sentinel , 这一功能是通过向频道 sentinel:hello 发送信息来实现的。
同样的,你也不必手动列出 Master下的所有Slave, 因为 Sentinel 可以通过询问主服务器来获得所有Slave的信息。
客户端可以将 Sentinel 看作是一个只提供了订阅功能的 Redis 服务器: 可以通过订阅给定的频道来获取相应的事件提醒。
比如,名为 +sdown 的频道就可以接收所有实例进入主观下线(SDOWN)状态的事件。Sentinel通过订阅Sentinel列表中的其他Sentinel的+sdown频道,接受所有进入主观下线的服务器事件,对于Master来说,进一步确定是否需要进入客观下线。
SLAVEOF NO ONE
命令,让它转变为主服务器。Sentinel 选择新的Master的规则:
Redis Sentinel 严重依赖计算机的时间功能: 比如说, 为了判断一个实例是否可用, Sentinel 会记录这个实例最后一次相应 PING 命令的时间, 并将这个时间和当前时间进行对比, 从而知道这个实例有多长时间没有和 Sentinel 进行任何成功通讯。
不过, 一旦计算机的时间功能出现故障, 或者计算机非常忙碌, 又或者进程因为某些原因而被阻塞时, Sentinel 可能也会跟着出现故障。
TILT 模式是一种特殊的保护模式: 当 Sentinel 发现系统有些不对劲时, Sentinel 就会进入 TILT 模式。
那它是如何发现不对劲的呢?
Sentinel 的时间中断器默认每秒执行 10 次, 所以我们预期时间中断器的两次执行之间的间隔为 100 毫秒左右。
基于这个特性,Sentinel 的做法是, 记录上一次时间中断器执行时的时间, 并将它和这一次时间中断器执行的时间进行对比:
当 Sentinel 进入 TILT 模式时, 它仍然会继续监视所有目标, 但是:
如果 TILT 可以正常维持 30 秒钟, 那么 Sentinel 退出 TILT 模式。
[1] Redis Sentinel Documentation.