在主从复制架构这篇blog里聊到了哨兵模式,在复杂的多主复合架构里,哨兵是必不可少的,所以这篇blog来详细解读下什么是哨兵模式,以及哨兵模式是怎么运行的。
哨兵模式概述
了解哨兵之前先了解一个概念,主从复制里的主从切换。
主从切换技术
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵模式
为了解决主从切换的各种问题,Redis使用了哨兵模式来进行处理。哨兵是一个分布式系统,用于对主从结构中的每台服务器进行监控,当master出现故障时通过选票机制选取新的master,并将每台slave连接到新的master上
哨兵的三个主要作用如下:
- 监控:通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。master存活检测,master与slave运行检测
- 通知:当被监控的服务器发生故障时,向外发出通知【其它哨兵,客户端】
- 自动故障转移:当哨兵监测到master宕机,会断开master与slave的连接,选取一个新的master【自动将slave切换成master】,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机连接到新master,通知所有客户端新的服务器地址【让用户无感知】。
以上就是哨兵的主要作用。其实哨兵也是一台Redis服务器,只不过不对外提供数据服务,这和kafka的zookeeper以及ElasticSearch的候选主节点承担类似的作用一样,就是集群的管理者。只不过Redis这个名字比较洋气,叫做哨兵。通常哨兵为单数,一般配置3个起。
启用哨兵模式
其实在Redis里我们是可以看到这样一个配置文件的,只不过我们之前没有操作过,那就是哨兵的配置文件,其实哨兵也有一个自己的配置文件
配置文件修改
我们可以看下这个配置文件里的关键信息:
[root@192 redis-6.0.8]# cat sentinel.conf | grep -v "#" | grep -v "^$"
可以查看到如下的配置
port 26379 //哨兵端口号
daemonize no //守护进程关闭
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp //文件存放目录
sentinel monitor mymaster 127.0.0.1 6379 2 //master端口号,以及只要有2个哨兵认为主机宕机即认定主机宕机n/2+1
sentinel down-after-milliseconds mymaster 30000 //哨兵在30秒内监测不到主机存活即认为宕机
sentinel parallel-syncs mymaster 1 //数据同步时默认同时同步一台,越小性能越差越稳定
sentinel failover-timeout mymaster 180000 //数据同步时超过180秒即3分钟则认为超时
sentinel deny-scripts-reconfig yes
我们调整为如下配置去操作:
port 26379 //哨兵端口号
daemonize no //守护进程关闭
dir /root/redis-6.0.8/data/ //文件存放目录
sentinel monitor mymaster 127.0.0.1 6379 2 //master端口号,以及只要有2个哨兵认为主机宕机即认定主机宕机n/2+1
sentinel down-after-milliseconds mymaster 30000 //哨兵在30秒内监测不到主机存活即认为宕机
sentinel parallel-syncs mymaster 1 //数据同步时默认同时同步一台,越小性能越差越稳定
sentinel failover-timeout mymaster 180000 //数据同步时超过180秒即3分钟则认为超时
在conf里新建如下几个文件,我们在三个端口布三个哨兵:6379、6380、6381。
哨兵配置文件
sentinel-26379.conf的配置为:
port 26379
dir /root/redis-6.0.8/data/
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel down-after-milliseconds tmlmaster 30000
sentinel parallel-syncs tmlmaster 1
sentinel failover-timeout tmlmaster 180000
sentinel-26380.conf的配置为:
port 26380
dir /root/redis-6.0.8/data/
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel down-after-milliseconds tmlmaster 30000
sentinel parallel-syncs tmlmaster 1
sentinel failover-timeout tmlmaster 180000
sentinel-26381.conf的配置为:
port 26381
dir /root/redis-6.0.8/data/
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel down-after-milliseconds tmlmaster 30000
sentinel parallel-syncs tmlmaster 1
sentinel failover-timeout tmlmaster 180000
同时我们还需要搭建好主从架构,缺一台6381服务器,那么我们新增一个配置即可,新增后可以看到我们的主从配置为:
主从配置文件
这样我们所有的配置就已经配置完毕了,一主二从,主为6379,从为6380和6381:
主master的配置文件:redis-6379.conf
port 6379
daemonize no
#logfile "redis-6379.log"
bind 127.0.0.1
dir /root/redis-6.0.8/data/
dbfilename dump-6379.rdb
rdbcompression yes
rdbchecksum yes
save 10 2
appendonly yes
appendfilename appendonly-6379.aof
appendfsync everysec
从slave1的配置文件:redis-6380.conf
port 6380
daemonize no
#logfile "redis-6380.log"
dir /root/redis-6.0.8/data/
dbfilename dump-6380.rdb
rdbcompression yes
rdbchecksum yes
slaveof 127.0.0.1 6379
repl-backlog-size 100mb
slave-server-stale-data no
从slave2的配置文件:redis-6381.conf
port 6381
daemonize no
#logfile "redis-6381.log"
dir /root/redis-6.0.8/data/
dbfilename dump-6381.rdb
rdbcompression yes
rdbchecksum yes
slaveof 127.0.0.1 6379
repl-backlog-size 100mb
slave-server-stale-data no
整体配置文件查看
[root@192 config]# ll
总用量 24
-rw-r--r-- 1 root root 234 10月 31 17:53 redis-6379.conf
-rw-r--r-- 1 root root 211 11月 1 10:37 redis-6380.conf
-rw-r--r-- 1 root root 211 11月 1 16:12 redis-6381.conf
-rw-rw-r-- 1 root root 212 11月 1 16:08 sentinel-26379.conf
-rw-rw-r-- 1 root root 212 11月 1 16:08 sentinel-26380.conf
-rw-rw-r-- 1 root root 212 11月 1 16:08 sentinel-26381.conf
[root@192 config]#
启动服务器及哨兵
启动顺序为先启动主服务器,然后启动从服务器,最后启动哨兵。先把历史的redis相关的进程都干掉,重新开始启动:
[root@192 config]# ps -ef | grep redis-
root 62045 61684 0 10:21 pts/2 00:00:18 redis-server 127.0.0.1:6379
root 67659 61734 0 16:24 pts/3 00:00:00 grep --color=auto redis-
[root@192 config]# kill -s 9 62045
[root@192 config]# kill -s 9 61684
[root@192 config]# ps -ef | grep redis-
root 67667 61734 0 16:24 pts/3 00:00:00 grep --color=auto redis-
[root@192 config]#
启动主从服务器
分别启动主从服务器后可以看到主服务器上打出如下日志,证明主从连接成功:
67863:M 01 Nov 2020 16:28:07.340 * Replica 127.0.0.1:6380 asks for synchronization
67863:M 01 Nov 2020 16:28:07.340 * Full resync requested by replica 127.0.0.1:6380
67863:M 01 Nov 2020 16:28:07.340 * Replication backlog created, my new replication IDs are '4944f27b383bcf43ff80f60187d34eb1f854007f' and '0000000000000000000000000000000000000000'
67863:M 01 Nov 2020 16:28:07.340 * Starting BGSAVE for SYNC with target: disk
67863:M 01 Nov 2020 16:28:07.340 * Background saving started by pid 67892
67892:C 01 Nov 2020 16:28:07.341 * DB saved on disk
67892:C 01 Nov 2020 16:28:07.341 * RDB: 0 MB of memory used by copy-on-write
67863:M 01 Nov 2020 16:28:07.434 * Background saving terminated with success
67863:M 01 Nov 2020 16:28:07.435 * Synchronization with replica 127.0.0.1:6380 succeeded
67863:M 01 Nov 2020 16:28:23.060 * Replica 127.0.0.1:6381 asks for synchronization
67863:M 01 Nov 2020 16:28:23.061 * Full resync requested by replica 127.0.0.1:6381
67863:M 01 Nov 2020 16:28:23.061 * Starting BGSAVE for SYNC with target: disk
67863:M 01 Nov 2020 16:28:23.061 * Background saving started by pid 67905
67905:C 01 Nov 2020 16:28:23.061 * DB saved on disk
67905:C 01 Nov 2020 16:28:23.062 * RDB: 0 MB of memory used by copy-on-write
67863:M 01 Nov 2020 16:28:23.148 * Background saving terminated with success
67863:M 01 Nov 2020 16:28:23.148 * Synchronization with replica 127.0.0.1:6381 succeeded
启动哨兵
首先预置开启各个会话准备进行连接:
启动哨兵的命令略有不同,redis-sentinel sentinel-26379.conf
,启动后可以看到一个哨兵id:Sentinel ID
[root@192 ~]# cd redis-6.0.8/config/
[root@192 config]# redis-sentinel sentinel-26379.conf
68018:X 01 Nov 2020 16:31:52.415 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
68018:X 01 Nov 2020 16:31:52.415 # Redis version=6.0.8, bits=64, commit=00000000, modified=0, pid=68018, just started
68018:X 01 Nov 2020 16:31:52.415 # Configuration loaded
68018:X 01 Nov 2020 16:31:52.415 * Increased maximum number of open files to 10032 (it was originally set to 1024).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.8 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 68018
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
68018:X 01 Nov 2020 16:31:52.416 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
68018:X 01 Nov 2020 16:31:52.417 # Sentinel ID is d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3
68018:X 01 Nov 2020 16:31:52.417 # +monitor master tmlmaster 127.0.0.1 6379 quorum 2
68018:X 01 Nov 2020 16:31:52.418 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:31:52.419 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
同时可以看到我们的主从信息。启动后我们使用哨兵客户端进行连接:
[root@192 ~]# cd redis-6.0.8/
[root@192 redis-6.0.8]# redis-cli -p 26379
127.0.0.1:26379> set name tml
(error) ERR unknown command `set`, with args beginning with: `name`, `tml`,
127.0.0.1:26379> info
# Server
redis_version:6.0.8
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:4c35dfe260ddf15c
redis_mode:sentinel
os:Linux 3.10.0-1127.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:9.3.1
process_id:68018
run_id:cd9b376fa25c4c68e42033ba249c16a12a0dc015
tcp_port:26379
uptime_in_seconds:183
uptime_in_days:0
hz:16
configured_hz:10
lru_clock:10384175
executable:/root/redis-6.0.8/config/redis-sentinel
config_file:/root/redis-6.0.8/config/sentinel-26379.conf
io_threads_active:0
# Clients
connected_clients:1
client_recent_max_input_buffer:2
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0
# CPU
used_cpu_sys:0.260898
used_cpu_user:0.055376
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
# Stats
total_connections_received:1
total_commands_processed:0
instantaneous_ops_per_sec:0
total_net_input_bytes:63
total_net_output_bytes:131
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
expire_cycle_cpu_milliseconds:3
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
tracking_total_keys:0
tracking_total_items:0
tracking_total_prefixes:0
unexpected_error_replies:0
total_reads_processed:3
total_writes_processed:2
io_threaded_reads_processed:0
io_threaded_writes_processed:0
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=tmlmaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=1
127.0.0.1:26379>
可以看到我们哨兵是不能执行数据操作的,同时可以看到哨兵的一些配置信息。在哨兵启用监控后,哨兵的配置文件发生了变化,
sentinel-26379.conf的配置为
port 26379
dir "/root/redis-6.0.8/data"
sentinel myid d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3
sentinel deny-scripts-reconfig yes
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel config-epoch tmlmaster 0
# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* +@all
sentinel leader-epoch tmlmaster 0
sentinel known-replica tmlmaster 127.0.0.1 6380
sentinel known-replica tmlmaster 127.0.0.1 6381
sentinel current-epoch 0
多了一些哨兵识别发现的主从机器,记录了他们的ip和端口号。此时如果我们再启动哨兵2和哨兵3后,哨兵们会相互发现彼此,例如我这里启动一个哨兵2和哨兵3,从哨兵3的启动记录可以看到
68411:X 01 Nov 2020 16:45:58.132 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
68411:X 01 Nov 2020 16:45:58.133 # Sentinel ID is 2adb9e95cee7937e77dc42eb018504d94c7ed433
68411:X 01 Nov 2020 16:45:58.133 # +monitor master tmlmaster 127.0.0.1 6379 quorum 2
68411:X 01 Nov 2020 16:45:58.133 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6379
68411:X 01 Nov 2020 16:45:58.134 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
68411:X 01 Nov 2020 16:45:58.521 * +sentinel sentinel f1b5da2c58c4c45929178484718230114bdf090a 127.0.0.1 26380 @ tmlmaster 127.0.0.1 6379
68411:X 01 Nov 2020 16:45:59.969 * +sentinel sentinel d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3 127.0.0.1 26379 @ tmlmaster 127.0.0.1 6379
不仅可以看到自己的哨兵id还可以识别到已经启动的哨兵1的id,同时再去看哨兵1的日志,多了两行新加进来的哨兵2和哨兵3的id:
68018:X 01 Nov 2020 16:31:52.416 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
68018:X 01 Nov 2020 16:31:52.417 # Sentinel ID is d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3
68018:X 01 Nov 2020 16:31:52.417 # +monitor master tmlmaster 127.0.0.1 6379 quorum 2
68018:X 01 Nov 2020 16:31:52.418 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:31:52.419 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:39:26.885 * +sentinel sentinel f1b5da2c58c4c45929178484718230114bdf090a 127.0.0.1 26380 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:46:00.198 * +sentinel sentinel 2adb9e95cee7937e77dc42eb018504d94c7ed433 127.0.0.1 26381 @ tmlmaster 127.0.0.1 6379
此时我们再去查看配置文件,也多了两行发现的哨兵id:
sentinel-26379.conf 的配置为
port 26379
dir "/root/redis-6.0.8/data"
sentinel myid d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3
sentinel deny-scripts-reconfig yes
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel config-epoch tmlmaster 0
# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* +@all
sentinel leader-epoch tmlmaster 0
sentinel known-replica tmlmaster 127.0.0.1 6380
sentinel known-replica tmlmaster 127.0.0.1 6381
sentinel known-sentinel tmlmaster 127.0.0.1 26381 2adb9e95cee7937e77dc42eb018504d94c7ed433
sentinel known-sentinel tmlmaster 127.0.0.1 26380 f1b5da2c58c4c45929178484718230114bdf090a
sentinel current-epoch 0
sentinel-26380conf 的配置为
port 26380
dir "/root/redis-6.0.8/data"
sentinel myid f1b5da2c58c4c45929178484718230114bdf090a
sentinel deny-scripts-reconfig yes
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel config-epoch tmlmaster 0
# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* +@all
sentinel leader-epoch tmlmaster 0
sentinel known-replica tmlmaster 127.0.0.1 6380
sentinel known-replica tmlmaster 127.0.0.1 6381
sentinel known-sentinel tmlmaster 127.0.0.1 26381 2adb9e95cee7937e77dc42eb018504d94c7ed433
sentinel known-sentinel tmlmaster 127.0.0.1 26379 d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3
sentinel current-epoch 0
sentinel-26381conf 的配置为
port 26381
dir "/root/redis-6.0.8/data"
sentinel myid 2adb9e95cee7937e77dc42eb018504d94c7ed433
sentinel deny-scripts-reconfig yes
sentinel monitor tmlmaster 127.0.0.1 6379 2
sentinel config-epoch tmlmaster 0
# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* +@all
sentinel leader-epoch tmlmaster 0
sentinel known-replica tmlmaster 127.0.0.1 6380
sentinel known-replica tmlmaster 127.0.0.1 6381
sentinel known-sentinel tmlmaster 127.0.0.1 26379 d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3
sentinel known-sentinel tmlmaster 127.0.0.1 26380 f1b5da2c58c4c45929178484718230114bdf090a
sentinel current-epoch 0
也就是说,监控同一主master的服务器,不仅会发现其从服务器还会发现彼此具有同一监听任务的哨兵
查看主从工作状态
主从模式搭建好我们首先看下能不能正常玩儿,我们先从主的客户端塞条数据进去,然后从从客户端取数据看能不能取到,往主里set数据
[root@192 redis-6.0.8]# redis-cli -p 6379
127.0.0.1:6379> set mylove guochengyu
OK
127.0.0.1:6379>
从从机get数据:
[root@192 ~]# cd redis-6.0.8
[root@192 redis-6.0.8]# redis-cli -p 6380
127.0.0.1:6380> get mylove
"guochengyu"
127.0.0.1:6380>
说明主从架构是完好的,接下来我们测验下哨兵如何选举和切换
宕机主查看哨兵工作
首先我们停掉主的服务器来查看,我们先停掉主服务器:
67863:M 01 Nov 2020 16:28:23.148 * Synchronization with replica 127.0.0.1:6381 succeeded
^C67863:signal-handler (1604220955) Received SIGINT scheduling shutdown...
67863:M 01 Nov 2020 16:55:55.524 # User requested shutdown...
67863:M 01 Nov 2020 16:55:55.524 * Calling fsync() on the AOF file.
67863:M 01 Nov 2020 16:55:55.524 * Saving the final RDB snapshot before exiting.
67863:M 01 Nov 2020 16:55:55.524 * DB saved on disk
67863:M 01 Nov 2020 16:55:55.524 # Redis is now ready to exit, bye bye...
[root@192 config]#
查看哨兵打出来的服务日志:
68018:X 01 Nov 2020 16:56:25.582 # +sdown master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:25.683 # +odown master tmlmaster 127.0.0.1 6379 #quorum 2/2
68018:X 01 Nov 2020 16:56:25.683 # +new-epoch 1
68018:X 01 Nov 2020 16:56:25.683 # +try-failover master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:25.684 # +vote-for-leader d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3 1
68018:X 01 Nov 2020 16:56:25.686 # f1b5da2c58c4c45929178484718230114bdf090a voted for d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3 1
68018:X 01 Nov 2020 16:56:25.686 # 2adb9e95cee7937e77dc42eb018504d94c7ed433 voted for d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3 1
68018:X 01 Nov 2020 16:56:25.786 # +elected-leader master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:25.786 # +failover-state-select-slave master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:25.848 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:25.849 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:25.925 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:26.788 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:26.788 # +failover-state-reconf-slaves master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:26.864 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:27.826 # -odown master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:27.827 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:27.827 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:27.898 # +failover-end master tmlmaster 127.0.0.1 6379
68018:X 01 Nov 2020 16:56:27.898 # +switch-master tmlmaster 127.0.0.1 6379 127.0.0.1 6381
68018:X 01 Nov 2020 16:56:27.898 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6381
68018:X 01 Nov 2020 16:56:27.898 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
可以看到新的主为6381机器,接下来把6379和6380连接到6381上,然后因为6379掉线,就踢掉6379。
哨兵模式工作原理
我们知道哨兵的三个工作顺序是三个阶段:监控阶段-通知阶段-故障转移阶段
监控阶段
监听阶段主要就是建立一个监听频道:
详细流程如下,一个哨兵在连接master后会记录哨兵状态,同时master也会记录哨兵,接下来哨兵观察是否有其它哨兵,如果有连接到其它哨兵并同步状态,然后会同步获取slave的状态,最终哨兵之间组成网络,互相同步消息。
通知阶段
这个阶段是维护一个长期信息对等的阶段,每个哨兵会将自己获取到的信息同步给其它哨兵。
故障转移阶段
这部分需要分为三个阶段,确定master宕机,选举哨兵leader,哨兵leader选择新master,主从切换:
判定master宕机
如果其中一个哨兵认为master挂了**【主观下线】,会将信息在哨兵集群中进行同步,此时其它哨兵会去检测master是否确实挂了,如果超过半数以上哨兵认为master挂了,则该master被认定挂了【客观下线】**:
选举哨兵leader
当认定master客观下线后,就需要进行故障转移,此时需要推举一个哨兵去处理故障转移事项。
这个时候就需要竞选处理故障的哨兵,一个redis服务被判断为客观下线时,多个监视该服务的sentinel协商,选举一个领头sentinel,对该redis服务进行故障转移操作。选举领头sentinel 遵循以下规则:
- 所有的sentinel都有公平被选举成领头的资格。
- 所有的sentinel都有且只有一次将某个sentinel选举成领头的机会(在一轮选举中),一旦选举某个sentinel为领头,不能更改。
- sentinel设置领头sentinel是先到先得,一旦当前sentinel设置了领头sentinel,以后要求设置sentinel为领头请求都会被拒绝。
- 每个发现服务客观下线的sentinel,都会要求其他sentinel将自己设置成领头。
- 当一个sentinel(源sentinel)向另一个sentinel(目sentinel)发送is-master-down-by-addr ip port current_epoch runid命令的时候,runid参数不是*,而是sentinel运行id,就表示源sentinel要求目标sentinel选举其为领头。
- 源sentinel会检查目标sentinel对其要求设置成领头的回复,如果回复的leader_runid和leader_epoch为源sentinel,表示目标sentinel同意将源sentinel设置成领头。
- 如果某个sentinel被半数以上的sentinel设置成领头,那么该sentinel既为领头。
- 如果在限定时间内,没有选举出领头sentinel,暂定一段时间,再选举。
那么该选谁由什么决定呢?
- 每个做主观下线的sentinel节点向其他sentinel节点发送is-master-down-by-addr ip port current_epoch runid命令,要求将它设置为领导者。
- 收到命令的sentinel节点如果还没有同意过其他的sentinel发送的命令(还未投过票),那么就会同意,否则拒绝。
- 如果该sentinel节点发现自己的票数已经过半且达到了quorum的值,就会成为领导者
- 如果这个过程出现多个sentinel成为领导者,则会等待一段时间重新选举。
总结一句话就是谁快速的发出了选举邀请就有大概率被选上,就是先到先得机制
哨兵leader选择新master
哨兵leader处理故障时,首先要选举一个备选master,要pass掉【不在线的、响应慢的、与原master断开时间久的】在进行了这些排除后,执行优先原则:
- 优先级:sentinel首先会根据slaves的优先级来进行排序,优先级越小排名越靠前。
- offset:如果优先级相同,则查看复制的下标,哪个从master接收的复制数据多,哪个就靠前。offset靠后的。
- 进程runid:如果优先级和下标都相同,就选择进程ID较小的那个。
一个redis无论是master还是slave,都必须在配置中指定一个slave优先级
主从切换
一旦选举出了master之后,就需要按如下方式操作进行主从切换:
- 向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为主服务器。
- 通过发布与订阅功能, 将更新后的配置传播给所有其他 Sentinel , 其他 Sentinel 对它们自己的配置进行更新。
- 向已下线主服务器的从服务器发送 SLAVEOF 命令, 让它们去复制新的主服务器。
- 当所有从服务器都已经开始复制新的主服务器时, 领头 Sentinel 终止这次故障迁移操作。
以上就是整个故障转移的过程。我们再回去看日志可以看到这些过程:
68411:X 01 Nov 2020 16:56:25.605 # +sdown master tmlmaster 127.0.0.1 6379
68411:X 01 Nov 2020 16:56:25.686 # +new-epoch 1
68411:X 01 Nov 2020 16:56:25.686 # +vote-for-leader d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3 1
68411:X 01 Nov 2020 16:56:25.689 # +odown master tmlmaster 127.0.0.1 6379 #quorum 3/2
68411:X 01 Nov 2020 16:56:25.689 # Next failover delay: I will not start a failover before Sun Nov 1 17:02:26 2020
68411:X 01 Nov 2020 16:56:26.864 # +config-update-from sentinel d8a28dddef8b037ba99bd551ac9f0bde8ea5dde3 127.0.0.1 26379 @ tmlmaster 127.0.0.1 6379
68411:X 01 Nov 2020 16:56:26.864 # +switch-master tmlmaster 127.0.0.1 6379 127.0.0.1 6381
68411:X 01 Nov 2020 16:56:26.864 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6381
68411:X 01 Nov 2020 16:56:26.864 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
68411:X 01 Nov 2020 16:56:56.899 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
此时我们重新启动master会发现重新连接到了哨兵的监控体系:
68018:X 01 Nov 2020 16:56:27.898 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ tmlmaster 127.0.0.1 6381
68018:X 01 Nov 2020 16:56:27.898 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
68018:X 01 Nov 2020 16:56:57.923 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
68018:X 01 Nov 2020 17:34:58.481 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
68018:X 01 Nov 2020 17:35:08.420 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ tmlmaster 127.0.0.1 6381
昔日的master成为了一个slave连接到了现在的master6381上了。