Redis哨兵架构
一、Redis哨兵架构搭建
-
基于主从架构搭建一主两从架构
-
准备开启三个哨兵节点,这里演示安装26379
-
复制sentinel.conf到26379目录下(目录自己指定)
-
修改sentinel.conf配置
port 26379
daemonize yes
pidfile "/var/run/redis‐sentinel‐26379.pid"
logfile "26379.log"
dir "/usr/local/redis/26379/"
# sentinel monitor <master‐redis‐name> <master‐redis‐ip> <master‐redis‐port> <quorum>
# quorum是一个数字,指明当有多少个seninel(值一般为:sentinel总数/2+1)认为一个master失效时master才算失效
# mymaster 自己定义,客户端访问时需要
sentinel monitor mymaster 192.168.1.110 6379 2
- 启动哨兵实例
./bin/redis-sentinel ./26379/sentinel/sentinel.conf
./bin/redis-sentinel ./26380/sentinel/sentinel.conf
./bin/redis-sentinel ./26381/sentinel/sentinel.conf
- 查看哨兵是否启动成功
- info命令查看哨兵信息
- 查看sentinel.conf中的redis节点信息
二、Java中使用哨兵架构
哨兵架构模式中,客户端访问Redis服务,第一次从哨兵服务中获取Redis主节点信息,后续就会直接访问主节点,不会再通过哨兵代理访问,当主节点发送变化后,哨兵会立即将新的主节点信息通知客户端(客户端一般都订阅了哨兵发布的节点变动信息)
- 引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<!-- <scope>test</scope>-->
</dependency>
- 测试代码
/**
* Jedis连接哨兵架构
*/
public class JedisSentinelTest {
JedisSentinelPool jedisSentinelPool = null;
@Before
public void connect(){
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
String masterName = "mymaster";
Set<String> sentinels = new HashSet<>();
sentinels.add(new HostAndPort("192.168.1.110",26379).toString());
sentinels.add(new HostAndPort("192.168.1.110",26380).toString());
sentinels.add(new HostAndPort("192.168.1.110",26381).toString());
jedisSentinelPool = new JedisSentinelPool(masterName,sentinels,config,3000,null);
}
@Test
public void testSentinel(){
Jedis jedis = null;
try {
jedis=jedisSentinelPool.getResource();
System.out.println(jedis.set("sentinel","1"));
System.out.println(jedis.get("sentinel"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedis!=null){
jedis.close();
}
}
}
}
- 运行结果
三、哨兵机制原理
1. 哨兵机制解决了什么问题
哨兵主要是为了解决主从复制架构中出现宕机的情况,在主节点出现宕机时,哨兵会自动将主节点下的某个从节点升级为新的主节点
2. 哨兵配置文件详解
# sentinel monitor <master-name> <ip> <port> <quorum>
# 哨兵定时监控主节点<master-name><ip><port>,<quorum>表示哨兵判断主节点是否发生故障的票数
# 如果我们将<quorum>设置为2,就代表至少需要2个哨兵节点认为主节点故障了,才算是这个主节点客观下线了,一般设置为:(sentinel/2 + 1)
sentinel monitor mymaster 192.168.1.110 6379 2
sentinel down-after-milliseconds <master-name> <times>
3. 哨兵定时监控
- 每个哨兵节点每10秒会向主节点和从节点发送info命令获取最新拓扑结构图,哨兵配置时只需要配置对主节点的监控即可,通过向主节点发送info命令,获取从节点的信息,并且当有新的从节点时可以立马感知到
- 每个哨兵节点每2秒会像redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其他哨兵对主节点状态的判断,其实就是通过消息publish/subscribe来完成的
- 每一秒每个哨兵会向主节点,从节点,其他哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据
4. 主观下线与客观下线
4.1 主观下线
主观下线就是单个哨兵认为某个节点下线
哨兵会以每秒一次的频率向所有与其建立连接的实例(主节点,从节点,其他哨兵节点)发送ping命令,通过判断ping回复是否有效,还是无效来判断实例是否在线
4.2 客观下线
当主观下线的节点是主观节点时,认为主节点主观下线的哨兵节点会通过指令sentinel is-masterdown-by-addr寻求其他哨兵节点对主节点的判断,当主观下线的个数超过quorum个数,此时大部分哨兵节点会都同意下线,这就是客观下线
5. 哨兵leader选举流程
主节点被判定为客观下线后,就需要选取一个哨兵节点来完成故障转移的工作,选举哨兵leader的流程如下:
- 每个在线的哨兵节点都可以成为leader,当它确认主节点下线后,会向其他哨兵发送is-master-down-by-addr命令,征求判断并要求把自己设置为leader
- 其他哨兵接收到这个命令后,可以同意或者拒绝它成为领导者
- 如果哨兵发现自己在选举的过程中的票数大于等于(sentinel/2 +1)时,它将成为leader,如果没有,继续选举
6. 自动故障转移
6.1 选择新的主节点
哨兵状态数据结构中保存了主服务的所有从服务的信息,哨兵leader会按照一定的规则选择出一个从节点成为新的主节点,选择规则如下:
- 过滤掉主观下线的节点
- 选择slave-priority最高的节点,如果有就返回,没有就继续选择
- 选择出复制偏移量最大的节点,复制偏移量越大则数据复制的越完整,有则返回,没有则继续
- 选择run_id最小的节点
6.2 更新主从状态
通过slaveof no one命令,让选择出来的从节点变为主节点,并通过slaveof命令让其他节点成为从节点
将已下线的主节点设置为新主节点的从节点,当它恢复正常时,从新的主节点复制数据
其他下线的从节点恢复正常时,哨兵会向其发送slaveof命令,让他成为新主的从节点