美文网首页程序员
redis设计与实现-Sentinel

redis设计与实现-Sentinel

作者: 求索 | 来源:发表于2020-03-08 14:34 被阅读0次
    sentinel哨兵模式架构.png

    概念

    Sentinel只是一个运行在特殊模式下的Redis服务器,它使用了和普通模式不同的命令表,所以Sentinel模式能够使用的命令和普通Redis服务器能够使用的命令不同。用于redis主从架构环境中。

    原理

    • 三个定时任务

      1. 每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构
      2. Sentinel向Redis数据节点的sentinel:hello 频道发送信息,所有sentinel节点都订阅该频道,用于交换各个sentinel节点的状态,以及发现新节点。
      3. 每个sentinel向其它各个节点发送心跳检测
    • 主观下线

      当Sentinel将一个主服务器判断为主观下线时,它会向同样监视这个主服务器的其他Sentinel进行询问,看它们是否同意这个主服务器已经进入主观下线状态。

    • 客观下线

    当Sentinel收集到足够多的主观下线投票之后,它会将主服务器判断为客观下线,并发起一次针对主服务器的故障转移操作。

    主观下线并不一定下线,有可能是这台sentinel和主服务器之间存在通信故障,需要选举投票。

    • Sentinel领导者选举
    1. 所有在线的Sentinel都有被选为领头Sentinel的资格,换句话说,监视同一个主服务器的多个在线Sentinel中的任意一个都有可能成为领头Sentinel。
    2. 每次进行领头Sentinel选举之后,不论选举是否成功,所有Sentinel的配置纪元(configuration epoch)的值都会自增一次。配置纪元实际上就是一个计数器,并没有什么特别的。
    3. 在一个配置纪元里面,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改。
    4. 每个发现主服务器进入客观下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel。
    5. 当一个Sentinel(源Sentinel)向另一个Sentinel(目标Sentinel)发送SENTINEL is-master-down-by-addr命令,并且命令中的runid参数不是*符号而是源Sentinel的运行ID时,这表示源Sentinel要求目标Sentinel将前者设置为后者的局部领头Sentinel。
    6. Sentinel设置局部领头Sentinel的规则是先到先得:最先向目标Sentinel发送设置要求的源Sentinel将成为目标Sentinel的局部领头Sentinel,而之后接收到的所有设置要求都会被目标Sentinel拒绝。
    7. 目标Sentinel在接收到SENTINEL is-master-down-by-addr命令之后,将向源Sentinel返回一条命令回复,回复中的leader_runid参数和leader_epoch参数分别记录了目标Sentinel的局部领头Sentinel的运行ID和配置纪元。
    8. 源Sentinel在接收到目标Sentinel返回的命令回复之后,会检查回复中leader_epoch参数的值和自己的配置纪元是否相同,如果相同的话,
      那么源Sentinel继续取出回复中的leader_runid参数,如果leader_runid参数的值和源Sentinel的运行ID一致,那么表示目标Sentinel将源Sentinel设置成了局部领头Sentinel。
    9. 如果有某个Sentinel被半数以上的Sentinel设置成了局部领头Sentinel,那么这个Sentinel成为领头Sentinel。举个例子,在一个由10个Sentinel组成的Sentinel系统里面,只要有大于等于10/2+1=6个Sentinel将某个Sentinel设置为局部领头Sentinel,那么被设置的那个Sentinel就会成为领头Sentinel。
    10. 因为领头Sentinel的产生需要半数以上Sentinel的支持,并且每个Sentinel在每个配置纪元里面只能设置一次局部领头Sentinel,所以在一个配置纪元里面,只会出现一个领头Sentinel。
    11. 如果在给定时限内,没有一个Sentinel被选举为领头Sentinel,那么各个Sentinel将在一段时间之后再次进行选举,直到选出领头Sentinel为止。
    • 故障转移。
    1. 在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。
    2. 让已下线主服务器属下的所有从服务器改为复制新的主服务器。
    3. 将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。

    遇到问题汇总

    • 至少三个Sentinel在三个不同的盒子里
    • 单数个sentinel
    • 三个sentinel + 1 个master +2 个slave 最低测试环境

    其它问题

    • Sentinel在客户端盒子里,什么实现?

    java 访问方式

    1. 直接使用Jedis访问:
    //连接池配置
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(100);
    jedisPoolConfig.setMaxIdle(20);
    jedisPoolConfig.setMinIdle(5);
    //哨兵配置列表
    Set<String> sentinelList = new HashSet<String>(Arrays.asList(
            "127.0.0.1:16379",
            "127.0.0.1:26379",
            "127.0.0.1:36379"
    ));
    JedisSentinelPool pool = new JedisSentinelPool("sentinelsMasterName", sentinelList, jedisPoolConfig, "password");
    //获取客户端
    Jedis jedis = pool.getResource();
    jedis.set("demo", "100");
    String demoValue = jedis.get("demo");
    
    1. 使用 template 实现:
    RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
    String[] host = new String[]{
            "127.0.0.1:16379",
            "127.0.0.1:26379",
            "127.0.0.1:36379"
    };
    for(String redisHost : host){
        String[] item = redisHost.split(":");
        String ip = item[0];
        String port = item[1];
        configuration.addSentinel(new RedisNode(ip, Integer.parseInt(port)));
    }
    configuration.setMaster("RedisMasterName");
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(100);
    jedisPoolConfig.setMaxIdle(20);
    jedisPoolConfig.setMinIdle(5);
    
    JedisConnectionFactory jedisConnectionFactory =
        new JedisConnectionFactory(configuration, jedisPoolConfig);
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    //设置开启事务
    template.setEnableTransactionSupport(true);
    //set key serializer
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    template.setKeySerializer(stringRedisSerializer);
    template.setHashKeySerializer(stringRedisSerializer);
    
    template.setConnectionFactory(jedisConnectionFactory());
    template.afterPropertiesSet();
    

    相关文章

      网友评论

        本文标题:redis设计与实现-Sentinel

        本文链接:https://www.haomeiwen.com/subject/xpuldhtx.html