哨兵机制

  • 为什么需要哨兵机制
  • 哨兵机制的原理是什么
  • 如何配置哨兵机制
  • 总结
  • 问题
  • JavaAPI连接主从+ 哨兵


为什么需要哨兵机制

  • 在上面讲到的Redis集群-主从复制模式下
  • 可以实现容灾备份和读写分离
  • 但是做不到真正的高可用
  • 如Master挂了,整个集群就只能提供读服务了, 不能够提供写服务
  • 所以需要有一个新的方案来解决这个问题
  • 这个方案就是Redis提出的哨兵机制Sentinel

哨兵机制的原理是什么

  • 如下图:
  • 在配置好了Master-Slave的基础之上,再增加一些哨兵进程
  • 用来监控各个节点(主要监控当前的Master节点)
  • 当有节点(Master)挂了,就会从该Master下属的Slave节点中选取一个作为新的Master(选取规则就不一定,很随机)
  • 注意:哨兵只做监控和选择slave作为新的master,哨兵不是备胎不会替代成为master
  • 其实哨兵本质是就是一个后台进程,不断的和redis服务做通信/心态检测
  • redis集群模式redisson怎么配置 redis 集群 配置_服务器


如何配置哨兵机制

  • 1.三台机器修改哨兵配置文件
cd /export/servers/redis/conf

vim sentinel.conf
  • 2.node01上
#修改bind配置,每台机器修改为自己对应的主机名
bind node01  
#配置sentinel服务后台运行
daemonize yes
#修改三台机器监控的主节点,现在主节点是node01服务器
sentinel monitor mymaster node01 6379 2
#sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,node01代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
  • 2.node02上
#修改bind配置,每台机器修改为自己对应的主机名
bind node02  
#配置sentinel服务后台运行
daemonize yes
#修改三台机器监控的主节点,现在主节点是node01服务器
sentinel monitor mymaster node01 6379 2
#sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,node01代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
  • 3.node03上
#修改bind配置,每台机器修改为自己对应的主机名
bind node03 
#配置sentinel服务后台运行
daemonize yes
#修改三台机器监控的主节点,现在主节点是node01服务器
sentinel monitor mymaster node01 6379 2
#sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,node01代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
  • 4.重启三台redis
/export/servers/redis/bin/redis-server /export/servers/redis/conf/redis_6379.conf
  • 5.启动三台机器上的哨兵
/export/servers/redis/bin/redis-sentinel /export/servers/redis/conf/sentinel.conf
  • 6.查看启动情况
ps -ef | grep redis
  • 7.使用客户端连接
/export/servers/redis/bin/redis-cli -h node01

/export/servers/redis/bin/redis-cli -h node02

/export/servers/redis/bin/redis-cli -h node03
  • 8.查看各自的角色

  • 9.干掉node01上的master
ps -ef | grep redis

kill -9 redis-server的进程号
  • 10.过几秒观察发现node02或node03变成了master
  • 11.再次启动node01的redis会发现他只能做slave了

总结

  • 开发中一般都是 主从+ 哨兵 就可以满足大部分场景
  • 如果还需对redis做扩展可以再使用后面讲解的Redis集群-Cluster-终极版集群

问题

  • 之前是java代码连接的还是单机的redis
  • 那么开发中一般都是主从+ 哨兵 ,那么该如何连接呢?

JavaAPI连接主从+ 哨兵

@Test
    public void testMasterSlaveAndSentinel(){
        //1.创建连接池配置对象
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(100);//设置最大连接
        jedisPoolConfig.setMaxIdle(10);//设置最大空闲连接数
        jedisPoolConfig.setMinIdle(5);//设置最小空闲连接数
        jedisPoolConfig.setMaxWaitMillis(2000);//最大等待时间
        jedisPoolConfig.setTestOnCreate(true);
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setTestOnReturn(true);
        //2.创建连接池对象
        //准备哨兵地址,注意哨兵的默认端口是26379
        Set<String> sentinels = new HashSet<>(Arrays.asList("node01:26379","node02:26379","node03:26379"));
        JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels, jedisPoolConfig);
        //3.从连接池中获取连接对象jedis
        Jedis jedis = jedisSentinelPool.getResource();
        //4.操作
        String pong = jedis.ping();
        System.out.println(pong);
        //5.归还连接
        jedis.close();
    }
  • JedisUtil也可以进行相应的修改
package cn.hanjiaxiaozhi;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/7 16:43
 * Desc 封装jedis工具类
 * 工具类一般都是使用类名.方法名就可以使用
 * 也就是说不需要创建对象就可以使用,那么该类最好设计成一个抽象类,因为抽象类别人不能直接new
 * 然后里面的方法设置为静态方法即可,这样别人就可以直接使用类名.方法名
 */
public abstract class JedisUtil {
    //将连接池对象提取出来,只需要在类初始化的时候创建一次即可
    private static JedisSentinelPool jedisSentinelPool;

    //static静态代码块只会在类被加载(第一次被使用的时候执行)
    static{
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(100);//设置最大连接
        jedisPoolConfig.setMaxIdle(10);//设置最大空闲连接数
        jedisPoolConfig.setMinIdle(5);//设置最小空闲连接数
        jedisPoolConfig.setMaxWaitMillis(2000);//最大等待时间
        jedisPoolConfig.setTestOnCreate(true);
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setTestOnReturn(true);
        //jedisPool = new JedisPool(jedisPoolConfig, "node01", 6379);
        Set<String> sentinels = new HashSet<>(Arrays.asList("node01:26379","node02:26379","node03:26379"));
        jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels, jedisPoolConfig);

    }

    public static Jedis getJedis(){
        return jedisSentinelPool.getResource();
    }
}