redis学习(七)哨兵
哨兵
Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方式是无法接受的。
高可用性
当主节点出现故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。
Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。
如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。
整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
这里的分布式是指:Redis数据节点、Sentinel节点集合、客户端分布在多个物理节点的架构。
Redis Sentinel与Redis主从复制模式只是多了若干Sentinel节点,所以Redis Sentinel并没有针对Redis节点做了特殊处理。
在以上的架构中,如果redis主数据节点发生了故障,那么多个sentinel节点对主节点的故障达成一致,选举出了新的主节点,其中的一个sentinel节点作为领导节点,负责故障的转义,负责通知客户端节点的变动,下线故障节点,副节点升级为新的节点。
Redis Sentinel具有以下几个功能:
监控:Sentinel节点会定期检测Redis数据节点、其余Sentinel节点是否可达。
通知:Sentinel节点会将故障转移的结果通知给应用方。
主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
配置提供者:在Redis Sentinel结构中,客户端在初始化的时候连接的是Sentinel节点集合,从中获取主节点信息。
在进行启动时,可以使用ping命令检查是否启动成功,或者使用进程命令查看进程ps是否包含。
info sentinel可以查看哨兵的信息
Redis安装目录下有一个sentinel.conf,是默认的Sentinel节点配置文件。
port和dir分别代表Sentinel节点的端口和工作目录。
sentinel monitor
sentinel monitor <master-name> <ip> <port> <quorum>
Sentinel节点会定期监控主节点,所以从配置上必然也会有所体现,本配置说明Sentinel节点要监控的是一个名字叫做<master-name>
,ip地址和端口为<ip><port>
的主节点。<quorum>
代表要判定主节点最终不可达所需要的票数。
但实际上Sentinel节点会对所有节点进行监控,但是在Sentinel节点的配置中没有看到有关从节点和其余Sentinel节点的配置,那是因为Sentinel节点会从主节点中获取有关从节点以及其余Sentinel节点的相关信息
当所有节点启动后,配置文件中的内容发生了变化,体现在三个方面:
- Sentinel节点自动发现了从节点、其余Sentinel节点。
- 去掉了默认配置,例如parallel-syncs、failover-timeout参数。
- 添加了配置纪元相关参数。
自动发现从节点,添加相关配置 sentinel known-slave <master-name> <slave_id> <slave_port>
表明了主节点的从节点的配置
自动发现其他哨兵节点信息sentinel known-sentinel <master-name> <sentinel_id> <sentinel_port> hash
sentinel down-after-milliseconds
每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没有有效的回复,则判定节点不可达,<times>
(单位为毫秒)就是超时时间。
sentinel down-after-milliseconds <master-name> <times>
down-after-milliseconds虽然以
sentinel parallel-syncs
sentinel parallel-syncs <master-name> <nums>
当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数。
如果这个参数配置的比较大,那么多个从节点会向新的主节点同时发起复制操作,尽管复制操作通常不会阻塞主节点,但是同时向主节点发起复制,必然会对主节点所在的机器造成一定的网络和磁盘IO开销。
sentinel failover-timeout <master-name> <times>
ailover-timeout通常被解释成故障转移超时时间,但实际上它作用于故障转移的各个阶段。
选出合适从节点,晋升选出的从节点为主节点
命令其余的从节点复制新的主节点
等待原主节点恢复后命令他去复制新的主节点
如果这些节点的时间都超过了failover-timeout,表明故障转移失败
sentinel auth-pass <master-name> <password>
如果Sentinel监控的主节点配置了密码,sentinel auth-pass配置通过添加主节点的密码,防止Sentinel节点对主节点无法监控。
sentinel notification-script <master-name> <script-path>
sentinel notification-script的作用是在故障转移期间,当一些警告级别的Sentinel事件发生(指重要事件,例如-sdown:客观下线、-odown:主观下线)时,会触发对应路径的脚本,并向脚本发送相应的事件参数。
sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script的作用是在故障转移结束后,会触发对应路径的脚本,并向脚本发送故障转移结果的相关参数。
如何监控多个主节点?
配置方法也比较简单,只需要指定多个masterName来区分不同的主节点即可。
sentinel monitor master-business-1 10.10.xx.1 6379 2 //第一个主节点
sentinel down-after-milliseconds master-business-1 60000
sentinel failover-timeout master-business-1 180000
sentinel parallel-syncs master-business-1 1
sentinel monitor master-business-2 10.16.xx.2 6380 2 //第二个主节点
sentinel down-after-milliseconds master-business-2 10000
sentinel failover-timeout master-business-2 180000
sentinel parallel-syncs master-business-2 1
动态调整
sentinel set <param> <value>
只在当前sentinel节点有效,一经修改立刻生效,不会重启之后生效,这需要注意。
部署技巧
Sentinel节点不应该部署在一台物理“机器”上。
部署至少三个且奇数个的Sentinel节点。
API
命令行命令。
sentinel masters
等其余的API和配置项相同
sentinel客户端连接
Sentinel节点集合具备了监控、通知、自动故障转移、配置提供者若干功能,也就是说实际上最了解主节点信息的就是Sentinel节点集合,而各个主节点可以通过<master-name>
进行标识的,所以,无论是哪种编程语言的
客户端,如果需要正确地连接Redis Sentinel,必须有Sentinel节点集合和masterName
两个参数。
实现一个Redis Sentinel客户端的基本步骤:
- 遍历Sentinel节点集合获取一个可用的Sentinel节点,后面会介绍Sentinel节点之间可以共享数据,所以从任意一个Sentinel节点获取主节点信息都是可以的。
- 通过
sentinel get-master-addr-by-name master-name
这个API来获取对应主节点的相关信息 - 验证当前获取的“主节点”是真正的主节点,这样做的目的是为了防止故障转移期间主节点的变化
- 保持和Sentinel节点集合的“联系”,时刻获取关于主节点的相关“信息”
Jedis针
对Redis Sentinel给出了一个JedisSentinelPool,很显然这个连接池保存的连接还是针对主节点的。
public JedisSentinelPool(String masterName, Set<String> sentinels,
final GenericObjectPoolConfig poolConfig, final int connectionTimeout,
final int soTimeout,
final String password, final int database,
final String clientName)
/*
masterName——主节点名。
·sentinels——Sentinel节点集合。
·poolConfig——common-pool连接池配置。
·connectTimeout——连接超时。
·soTimeout——读写超时。
·password——主节点密码。
·database——当前数据库索引。
·clientName——客户端名
*/
/*
为每一个Sentinel节点单独启动一个线程,利用Redis的发布订阅功
能,每个线程订阅Sentinel节点上切换master的相关频道+switch-master。
*/
Jedis sentinelJedis = new Jedis(sentinelHost, sentinelPort);
// 客户端订阅Sentinel节点上"+switch-master"(切换主节点)频道
sentinelJedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
String[] switchMasterMsg = message.split(" ");
if (switchMasterMsg.length > 3) {
// 判断是否为当前masterName
if (masterName.equals(switchMasterMsg[0])) {
// 发现当前masterName发生switch,使用initPool重新初始化连接池
initPool(toHostAndPort(switchMasterMsg[3], switchMasterMsg[4]));
}
}
}
}, "+switch-master");
实现原理
Redis Sentinel的基本实现原理,具体包含以下几个方面:
Redis Sentinel的三个定时任务、主观下线和客观下线、Sentinel领导者选举、故障转移
三个定时任务
Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控。
每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构。
Sentinel节点通过info命令对结果进行解析就可以找到相应的从节点。
每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__
:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断。
<Sentinel节点IP> <Sentinel节点端口> <Sentinel节点runId> <Sentinel节点配置版本> <主节点名字> <主节点Ip> <主节点端口> <主节点配置版本>
每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
主观下线,客观下线
主观下线,一个sentinel节点认为 主节点下线
每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过
down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。
客观下线,当超过quorum个数的sentinel节点认为主节点下线
当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel is-master-down-by-addr
命令向其他Sentinel节点询问对主节点的判断,当超过<quorum>
个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定。
故障转移
Redis使用了Raft算法实现领导者选举,因为Raft算法相对比较抽象和复杂。
选举的过程非常快,基本上谁先完成客观下线,谁就是领导者。
sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>
ip:主节点IP。
port:主节点端口。
current_epoch:当前配置纪元。
runid:此参数有两种类型,不同类型决定了此API作用的不同。
当runid等于“*”时,作用是Sentinel节点直接交换对主节点下线的判定。
当runid等于当前Sentinel节点的runid时,作用是当前Sentinel节点希望目
标Sentinel节点同意自己成为领导者的请求
1 选出最好的从节点
2 sentinel领导者会对选出的从节点执行slaveof no one
3 Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel-syncs参数有关
4 Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点
开发与运维的问题
模拟故障的方法有很多
强制杀掉对应节点的进程号,或者使用redis的debug sleep命令,或者使用shutdown命令。
节点运维
下线节点,在任意一个sentinel节点上执行sentinel failover <master name>
添加节点
添加从节点使用,slaveof{masterIp}{masterPort}的配置,使用redis-server启动即可,它将被Sentinel节点自动发现
添加Sentinel节点
添加sentinel monitor主节点的配置,使用redis-sentinel启动即可,它将被其余Sentinel节点自动发现
Sentinel节点只支持如下命令:ping、sentinel、subscribe、unsubscribe、psubscribe、punsubscribe、publish、info、role、client、shutdown。
sentinel下的读写分离框架图