Redis的哨兵详解
一、Redis Sentinel
如果主节点凌晨3 点突发宕机怎么办?就坐等运维从床上爬起来,然后手工进行从主切换,再通知所有的程序把地址统统改一遍重新上线么?毫无疑问,这样的人工运维效率太低,事故发生时估计得至少 1 个小时才能缓过来。如果是一个大型公司,这样的事故足以上新闻了。
所以我们必须有一个高可用方案来抵抗节点故障,当故障发生时可以自动进行从主切换,程序可以不用重启,运维可以继续睡大觉,仿佛什么事也没发生一样。Redis 官方提供了这样一种方案 —— Redis Sentinel(哨兵)。
二、Redis Sentinel简介
Sentinel(哨兵)进程是用于监控redis集群中Master主服务器工作的状态,在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用,其已经被集成在redis2.6+的版本中,Redis的哨兵模式到了2.8版本之后就稳定了下来。一般在生产环境也建议使用Redis的2.8版本的以后版本。哨兵(Sentinel) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master主服务器是否下线的信息,并使用投票协议(Agreement Protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。每个哨兵(Sentinel)进程会向其它哨兵(Sentinel)、Master、Slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定配置时间(可配置的)内未得到回应,则暂时认为对方已掉线,也就是所谓的”主观认为宕机” ,英文名称:Subjective Down,简称SDOWN。有主观宕机,肯定就有客观宕机。当“哨兵群”中的多数Sentinel进程在对Master主服务器做出 SDOWN 的判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的Master Server下线判断,这种方式就是“客观宕机”,英文名称是:Objectively Down, 简称 ODOWN。通过一定的vote算法,从剩下的slave从服务器节点中,选一台提升为Master服务器节点,然后自动修改相关配置,并开启故障转移(failover)。
哨兵(sentinel) 虽然有一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel),哨兵(sentinel) 的一些设计思路和zookeeper非常类似。
Sentinel集群之间会互相通信,沟通交流redis节点的状态,做出相应的判断并进行处理,这里的主观下线状态和客观下线状态是比较重要的状态,它们决定了是否进行故障转移,可以 通过订阅指定的频道信息,当服务器出现故障得时候通知管理员,客户端可以将 Sentinel 看作是一个只提供了订阅功能的 Redis 服务器,你不可以使用 PUBLISH 命令向这个服务器发送信息,但你可以用 SUBSCRIBE 命令或者 PSUBSCRIBE 命令, 通过订阅给定的频道来获取相应的事件提醒。一个频道能够接收和这个频道的名字相同的事件。 比如说, 名为 +sdown 的频道就可以接收所有实例进入主观下线(SDOWN)状态的事件。
1、Sentinel(哨兵)进程的作用:
1】、监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
2】、提醒(Notification):当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
3】、自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master;当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master。Master和Slave服务器切换后,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的内容都会发生相应的改变,即,Master主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换
2、Sentinel(哨兵)进程的工作方式:
1】、每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
2】、如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。
3】、如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。
4】、当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
5】、在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
6】、当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
7】、若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
三、Redis Sentinel搭建
准备3个redis和sentinel的配置文件
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# ll total 340 -rw-rw-r-- 1 root root 165586 Dec 12 2018 00-RELEASENOTES -rw-rw-r-- 1 root root 53 Dec 12 2018 BUGS -rw-rw-r-- 1 root root 1815 Dec 12 2018 CONTRIBUTING -rw-rw-r-- 1 root root 1487 Dec 12 2018 COPYING drwxr-xr-x 2 root root 4096 Apr 12 11:53 data drwxrwxr-x 6 root root 4096 Mar 11 21:03 deps -rw-r--r-- 1 root root 893 Mar 21 13:32 dump.rdb -rw-rw-r-- 1 root root 11 Dec 12 2018 INSTALL -rw-rw-r-- 1 root root 151 Dec 12 2018 Makefile -rw-rw-r-- 1 root root 4223 Dec 12 2018 MANIFESTO -rw-rw-r-- 1 root root 20543 Dec 12 2018 README.md -rw-r--r-- 1 root root 54 Apr 12 11:53 redis-6380.conf -rw-r--r-- 1 root root 76 Apr 12 11:53 redis-6381.conf -rw-rw-r-- 1 root root 58781 Apr 12 11:23 redis.conf -rw-r--r-- 1 root root 0 Mar 22 14:32 redis.log -rwxrwxr-x 1 root root 271 Dec 12 2018 runtest -rwxrwxr-x 1 root root 280 Dec 12 2018 runtest-cluster -rwxrwxr-x 1 root root 281 Dec 12 2018 runtest-sentinel -rw-r--r-- 1 root root 565 Apr 12 11:53 sentinel-26379.conf -rw-r--r-- 1 root root 565 Apr 12 11:53 sentinel-26380.conf -rw-r--r-- 1 root root 565 Apr 12 11:53 sentinel-26381.conf -rw-rw-r-- 1 root root 7921 Dec 12 2018 sentinel.conf drwxrwxr-x 3 root root 4096 Mar 11 21:07 src drwxrwxr-x 10 root root 4096 Dec 12 2018 tests drwxrwxr-x 8 root root 4096 Dec 12 2018 utils [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]#
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# cat sentinel.conf | grep -v "#" |grep -v "^$" port 26379 dir /tmp sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000 sentinel deny-scripts-reconfig yes [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# sed 's/26379/26380/g' sentinel-26379.conf > sentinel-26380.conf [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# cat sentinel-26380.conf port 26380 dir /root/redis-4.0.12/data/ sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000 sentinel deny-scripts-reconfig yes [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# sed 's/26379/26381/g' sentinel-26379.conf > sentinel-26381.conf [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# cat sentinel-26381.conf port 26381 dir /root/redis-4.0.12/data/ sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000 sentinel deny-scripts-reconfig yes
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# cat redis-6381.conf port 6381 daemonize no dir ./data/ slaveof 127.0.0.1 6379
首先启动redis的3台主从
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# redis-server redis.conf 7853:C 12 Apr 11:41:01.966 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 7853:C 12 Apr 11:41:01.967 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=7853, just started 7853:C 12 Apr 11:41:01.967 # Configuration loaded 7853:M 12 Apr 11:41:01.968 # Creating Server TCP listening socket *:6379: bind: Address already in use [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# kill -9 6209 [root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# redis-server redis.conf 7866:C 12 Apr 11:41:16.642 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 7866:C 12 Apr 11:41:16.643 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=7866, just started 7866:C 12 Apr 11:41:16.643 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.12 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 7866 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
6380服务
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# redis-server redis-6380.conf 7983:C 12 Apr 11:42:44.644 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 7983:C 12 Apr 11:42:44.644 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=7983, just started 7983:C 12 Apr 11:42:44.644 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.12 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6380 | `-._ `._ / _.-' | PID: 7983 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
6381服务
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# redis-server redis-6381.conf 8055:C 12 Apr 11:43:23.333 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 8055:C 12 Apr 11:43:23.333 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=8055, just started 8055:C 12 Apr 11:43:23.333 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.12 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6381 | `-._ `._ / _.-' | PID: 8055 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
再启动sentinel集群,剩下2台的启动我就不贴出来了。
[root@iZbp143t3oxhfc3ar7jey0Z redis-4.0.12]# redis-sentinel sentinel-26379.conf 8161:X 12 Apr 11:45:27.141 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 8161:X 12 Apr 11:45:27.141 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=8161, just started 8161:X 12 Apr 11:45:27.141 # Configuration loaded _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.12 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 8161 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 8161:X 12 Apr 11:45:27.142 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
当3台都启动好了,发现原来的sentinel的配置会多了一些信息。
Sentinel的原理
哨兵在进行主从切换过程中经历三个阶段
- 监控
- 通知
- 故障转移
第一阶段:
首先要获取各个哨兵在不在,通过ping的指令,还要获取matser信息,可以通过info命令,拿到它的runid和它的角色,再获取各个slave的详细信息
,也是通过info向各个slave获取信息,拿到各种信息,准备后面的主节点挂了有用,当第二个sentinel也要重复一样的事情,它存的SentinelState
中的sentinels的信息会比第一个多点,为了保证他们的信息一样,就会通过发布订阅一直用来同步,并且看对方在不在。
第二阶段:
sentinel会用redis发送hello信息,看各个服务在不在,拿到返回的信息然后,sentinel在与别的sentinel节点发布,进行信息的传播
第三阶段:
sentinel当发现master挂了以后,会标记成S_DOWN,剩下的sentinel就会也去发hello,去看master是不是真的挂了,当过了半数以后,就会标记成O_DOWN,即客观下线。
会在sentinel中发送信息,如:那个服务挂了,挂的端口,自己参加竞选的次数已经自己的runid,sentinel先接到别的节点的信息,就会把
票投给谁,到最后会得到一个投票的结论,如果票数一致,会继续投票,竞选次数加1,到最后选出老大
于是去开始进行选择matser,首选要选择选择在线的,然后一直发送hello,在pass响应慢的,
接下来与原master断开的时间久的,找到最近连接的,最后就是优选原则,偏移量,到最后
就是去找小的runid,就是当成master,然后与各个slave发送信息,与自己相连。
主从切换演示:我把master的服务停了。
下面就是sentinel的日志变化
由于之前的从不能进行写,现在去6380的客户端测试一下
[root@iZbp143t3oxhfc3ar7jey0Z ~]# redis-cli -p 6380 127.0.0.1:6380> get name "wgr" 127.0.0.1:6380> set n1 test OK 127.0.0.1:6380> get n1 "test" 127.0.0.1:6380> exit