携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
概述
哨兵(Sentienl)模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。
原理
底层原理是心跳检测(keep-alived),是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
应用场景
当主服务器宕机后,需要手动把一台从服务器切换为主服务器;这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,哨兵模式能够自动切换主从节点。因此,更多时候,我们优先考虑哨兵模式。
Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。
主从切换的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从节点切换为主节点。
这里的哨兵有两个作用
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
Redis配置哨兵模式
配置3个哨兵和1主2从的Redis服务器来演示这个过程。
| 服务类型 | 是否是主服务器 | IP地址 | 端口 |
|---|---|---|---|
| Redis | 是 | 192.168.11.128 | 6379 |
| Redis | 否 | 192.168.11.129 | 6379 |
| Redis | 否 | 192.168.11.130 | 6379 |
| Sentinel | - | 192.168.11.128 | 26379 |
| Sentinel | - | 192.168.11.129 | 26379 |
| Sentinel | - | 192.168.11.130 | 26379 |
除了监控各个Redis服务器之外,哨兵之间也会互相监控!
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover(故障转移)过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。主从切换要经历主观下线 => 客观下线的过程,即发现问题到解决问题,最终完成是==以客观下线为准==。
手动切换主从节点
如果主机断开了连接,我们可以使用slaveof no one让自己变成主机,其他节点就可以手动连接到最新的这个主节点!
127.0.0.1:6379> auth 123456
127.0.0.1:6379> slaveof no one # 将从机切换为主机,单向的
OK
选举算法
选举算法(Raft协议,Term(任期),RPC
Raft是用于实施分布式共识的协议,其中节点有三种状态:领导(Leader)、跟随(Follow)和候选(candidate)。所有的节点都以跟随者(Folllow)状态开始,没有收到的数据(即主节点宕机后)的跟随(从)节点将成为候选节点;然后候选人向其他节点请求投票,投票多的候选节点将成为领导人(Leader/Master),这个过程称为领导人选举。每一个任期(Term) 的开始都是一个选举过程,如果一个候选人赢得了选举,它就会在该任期的剩余时间担任领导人;如果没有(从节点票数相同)选出领导人,那么马上开始下一个任期。Term是为了**==解决数据同步问题==,Raft算法保证在给定的一个任期内最多是有==一个==领导人。后面Redis Cluster**(集群)使用了类似于Raft算法term(任期)的概念,称为epoch(==纪元==),用来给时间增加版本号(==自增ID==)。选举结束后,新主节点最后以广播的方式传播通知其他节点。那么,是以时间?权重?还是数据量为选举依据?如果选举时候选人的宕机时间一致呢?如果数据量也相同呢?
到底选举是以什么标准进行选举呢?即选票是什么
选举Leader的四大因素
1.跟master断开连接的时长
首先,如果一个slave跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master。
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
2.slave优先级
按照slave优先级进行排序,slave priority越低,优先级就越高,默认100,值最低成为主
3.复制offset
如果slave priority相同,那么看replica offset(复制偏移量),哪个slave复制了越多的数据,offset越靠后,优先级就越高
4.run id
如果上面两个条件都相同,那么选择一个run id(runid可以通过info命令查看,redis每次重启runid都会变动,是系统根据重启时间赋予的)比较小的那个slave;值越小,说明重启时间越靠前
Raft算法就是通过以上4点因素进行协商考虑进而选举出主节点(Master/Leader)的一种算法。
哨兵完成切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub**==发布/订阅==消息机制(具体实现:选举时将节点信息添加到日志条目中,选为Leader后提交给所有存活节点,完成同步,该过程称为==日志复制==**)。这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换之后,新的master配置是跟着新的==version==号的。其他的哨兵都是根据版本号的大小来更新自己的master配置的
使用哨兵
测试!
我们目前的状态还是一主二从!
1、配置哨兵配置文件 sentinel.conf
# 进入/user/local/bin/redis/redis-conf目录
cd redis-conf
# 创建并编辑sentinel配置文件,这里的1是选举的优先级,类似于思科的STP(Spaning Tree Protocol,生成树协议)用到的决策算法
vim sentinel.conf
# sentinel monitor 被监控的主机别名 host port priority(优先级,即需要主观判断挂了的节点数)
sentinel monitor master6379 127.0.0.1 6379 1
# 主密码,不设置的话不能动态切换(因为默认是将保护模式关闭,密码为空,设置了密码则需要配置)
sentinel auth-pass master6379 123456
后面的这个数字1(为优先级),代表主机挂了,slave投票看让谁接替主机;值越小,票数越多。票数最多的,就会成为主机!设置密码到哨兵配置文件中后,原来的所有节点均需要重新确定密码!
2、启动哨兵
[root@iZwz97gjh27h6busjtqk4jZ redis]# /usr/local/bin/redis/redis-sentinel redis-conf/sentinel.conf # 使用配置文件启动哨兵
165849:X 03 Jan 2021 05:17:58.167 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
165849:X 03 Jan 2021 05:17:58.167 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=165849, just started
165849:X 03 Jan 2021 05:17:58.167 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.9 (00000000/0) 64 bit
.-`` .-```. ```/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 165849
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
165849:X 03 Jan 2021 05:17:58.169 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
165849:X 03 Jan 2021 05:17:58.169 # Sentinel ID is 2c3f52bc006fcaabb305e3bd766bf242ca9a8c1c
165849:X 03 Jan 2021 05:17:58.169 # +monitor master master6379 127.0.0.1 6379 quorum 1
165849:X 03 Jan 2021 05:17:58.171 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:17:58.175 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ master6379 127.0.0.1 6379
注意事项
注意:如果主从服务器设置密码,需主从服务器密码保持一致,否则哨兵机制会失败!而且哨兵(Sentinel)要在主机存活时开启,否则会报Next failover delay: I will not start a failover before不会开启故障转移的错误。并且要是在监视当前主机,如果监控的端口不是主机也会报错。
-
在主从搭建好的基础上配置哨兵模式,无需修改主从配置的redis.conf,因为之前已配好
-
修改哨兵配置文件sentinel.conf
sentinel monitor mymaster 192.168.72.101 6379 1 # -主地址|主端口|参与选举个数配置基数3-2 sentinel auth-pass mymaster 12345678 # -主密码,不设置的话不能动态切换 -
原来鉴权过的哨兵中还需要==重新确认密码==(只有==选举成功==的主机与宕机重启的原主机需要,即主从切换的服务器需要)
127.0.0.1:6381> auth 123456 OK -
启动顺序(推荐)
主服务->从服务->哨兵监听
哨兵开启主要注意 主机存活时开启、主从节点密码一致、切换主机后连接需重新确认密码 这3个问题即可.
测试结果
# 哨兵监控日志
165849:X 03 Jan 2021 05:19:34.961 # +sdown master master6379 127.0.0.1 6379 # 主观宕机
165849:X 03 Jan 2021 05:19:34.961 # +odown master master6379 127.0.0.1 6379 #quorum 1/1 # 客观宕机
165849:X 03 Jan 2021 05:19:34.961 # +new-epoch 2 # 开启两个新纪元(选举周期)
165849:X 03 Jan 2021 05:19:34.961 # +try-failover master master6379 127.0.0.1 6379 # 开始故障迁移
165849:X 03 Jan 2021 05:19:34.965 # +vote-for-leader 2c3f52bc006fcaabb305e3bd766bf242ca9a8c1c 2 # 开始投票
165849:X 03 Jan 2021 05:19:34.965 # +elected-leader master master6379 127.0.0.1 6379 # 选举主节点
165849:X 03 Jan 2021 05:19:34.965 # +failover-state-select-slave master master6379 127.0.0.1 6379 # 故障迁移状态-选举主节点状态
165849:X 03 Jan 2021 05:19:35.031 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ master6379 127.0.0.1 6379 # 已选好从节点6381为新的主节点
165849:X 03 Jan 2021 05:19:35.031 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ master6379 127.0.0.1 6379 # 与原主节点进行交接,即两台服务器主从切换
165849:X 03 Jan 2021 05:19:35.098 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:19:35.501 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:19:35.501 # +failover-state-reconf-slaves master master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:19:35.587 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:19:36.535 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:19:36.535 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ master6379 127.0.0.1 6379
165849:X 03 Jan 2021 05:19:36.588 # +failover-end master master6379 127.0.0.1 6379 # 故障迁移完成
165849:X 03 Jan 2021 05:19:36.588 # +switch-master master6379 127.0.0.1 6379 127.0.0.1 6381 # 主节点切换完成
165849:X 03 Jan 2021 05:19:36.588 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ master6379 127.0.0.1 6381
165849:X 03 Jan 2021 05:19:36.588 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ master6379 127.0.0.1 6381
165849:X 03 Jan 2021 05:20:06.596 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ master6379 127.0.0.1 6381 # 最后再检测一次原主节点是否存活(主观宕机检测)
165849:X 03 Jan 2021 05:43:50.518 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ master6379 127.0.0.1 6381
165849:X 03 Jan 2021 05:44:00.504 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ master6379 127.0.0.1 6381 # 重新启动宕机的原主节点,哨兵会将原主节点切换为从节点
通过配置哨兵模式,将主节点宕机,一段时间后哨兵将剩下的从节点中选举出了一个从节点(6381服务器)作为主节点(Master)从而实现了主从节点的动态切换!
如果Master节点断开了,这个时候就会从从机中随机选择一个服务器!(Raft协议选举)
如果主机此时回来了,只能归并到新的主机下,当做从机,这就是哨兵模式的规则!
哨兵模式
哨兵优缺点
优点
- 哨兵集群,基于主从模式的,所有主从的优点,哨兵模式都有
- 主从可以自动切换,故障可以转移,系统的可用性就会更高
- 哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点
- redis 较难支持在线扩容,集群容量一旦到达上限时,在线扩容就会变得十分困难!
- 实现哨兵模式的配置其实是很麻烦的,里面有很多选择!
哨兵模式全部配置
哨兵模式的全部配置
1 # Example sentinel.conf
2
3 # 哨兵sentinel实例运行的端口 默认26379
4 port 26379
5
6 # 哨兵sentinel的工作目录
7 dir ./ 8
9 # 哨兵sentinel监控的redis主节点的 ip port
10 # master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
11 # quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
12 # sentinel monitor <master-name> <ip> <redis-port> <quorum>
13 sentinel monitor mymaster 127.0.0.1 6379 2
14
15 # 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
16 # 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
17 # sentinel auth-pass <master-name> <password>
18 sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
19
20 # 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
21 # sentinel down-after-milliseconds <master-name> <milliseconds>
22 sentinel down-after-milliseconds mymaster 30000
23
24 # 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
25 这个数字越小,完成failover所需的时间就越长,
26 但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
27 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
28 # sentinel parallel-syncs <master-name> <numslaves>
29 sentinel parallel-syncs mymaster 1
30
31 # 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
32 #1. 同一个sentinel对同一个master两次failover之间的间隔时间。
33 #2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
34 #3.当想要取消一个正在进行的failover所需要的时间。
35 #4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
36 # 默认三分钟
37 # sentinel failover-timeout <master-name> <milliseconds>
38 sentinel failover-timeout mymaster 180000
39
40 # SCRIPTS EXECUTION
41
42 #配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
43 #对于脚本的运行结果有以下规则:
44 #若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
45 #若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
46 #如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
47 #一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
48
49 #通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
50 这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
51 一个是事件的类型,
52 一个是事件的描述。
53 如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
54 #通知脚本
55 # sentinel notification-script <master-name> <script-path>
56 sentinel notification-script mymaster /var/redis/notify.sh
57
58 # 客户端重新配置主节点参数脚本
59 # 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
60 # 以下参数将会在调用脚本时传给脚本:
61 # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
62 # 目前<state>总是“failover”,
63 # <role>是“leader”或者“observer”中的一个。
64 # 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
65 # 这个脚本应该是通用的,能被多次调用,不是针对性的。
66 # sentinel client-reconfig-script <master-name> <script-path>
67 sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
哨兵模式常用配置
简化配置
# 基础配置
protected-mode no # 关闭保护模式
port 26479 # 端口
daemonize yes # 使用后台模式启动
pidfile "/var/run/redis-sentinel_26479.pid" # 进程id文件 logfile "/usr/local/redis/sentinel/sentinel_26479.log" # 日志文件 dir "/usr/local/redis/sentinel" # 工作目录
# 核心配置
1、 sentinel monitor <master-name> <ip> <port> <quorum>
# master-name:redis主节点昵称。
# ip:redis主机ip。
# port:redis主机端口。
# quorum:哨兵判断主节点是否发生故障的票数。如果设置为2,表示2个哨兵节点认为主节点发生了故障,一般设置为:哨兵节点数/2+1。
2、sentinel down-after-milliseconds <master-name> <times>
# 哨兵会定期的向redis节点发送ping命令来判断redis是否可达,若超过指定的times毫秒内还未得到pong回复,则判读该redis不可达。
3、sentinel parallel-syncs <master-name> <nums>
# 当redis主节点挂了后,哨兵会选出新的master,此时,剩余的slave会向新的master发起同步数据,这个设置表示允许并行同步的slave个数。
4、sentinel failover-timeout <master-name> <times>
# 进行故障转移时,如果超过设置的times毫秒(默认30s),表示故障转移失败。
5、sentinel auth-pass <master-name> <password>
# 如果redis主节点设置了密码,则需要进行这个配置。
# 配置redis主从复制、读写分离
# 配置思路:master配置文件不需要动,修改slave的配置文件。
1、replicaof <masterip> <masterport>
# 定义从节点连接主节点网络地址
2、masterauth <master-password>
# 如果master配置有密码,则需要配置这一行
3、replica-read-only yes
#表示slave中的数据是只读的(不用配置,默认为只读)
SpringBoot中使用哨兵模式
1.添加yml配置文件
spring:
redis:
database: 0
password: 12345678
sentinel:
master: mymaster
nodes: 192.168.43.243:26379,192.168.0.1:26479,192.168.0.1:26579
lettuce:
pool:
max-idle: 10
max-active: 20
min-idle: 5
max-wait: 10000ms
2.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
3.代码测试
@Autowired
private StringRedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("k3","v3");
}
执行结果
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6380> get k3
"v3"
127.0.0.1:6381> get k3
"v3"
4.注意事项
上面的pom.xml如果不加commons-pool2数据库会报以下错误:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisConnectionFactory' defined in class path resource [org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory]: Factory method 'redisConnectionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:645) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
错误提示的大概含义为:""数据库连接池没有实现,需要引入其它的实现jar包",我们找到问题的原因,添加指定的jar即可解决。
欢迎点赞关注评论,感谢观看ヾ(◍°∇°◍)ノ゙