Redis实践(八)-Sentinal

作者: 紫霞等了至尊宝五百年 | 来源:发表于2018-08-28 06:52 被阅读211次

    1

    目录

    2 主从复制高可用?


    故障出现主节点挂掉
    主从复制-mster宕掉故障处理

    3 Redis Sentinel 架构



    可监控多套

    4 安装与配置

    安装与配置

    Redis 主节点
    Redis 从节点
    Sentinel 主要配置

    5 安装与演示


    主节点配置
    快速生成从节点配置文件
    重定向
    打印检查配置文件
    启动

    6 客户端

    客户端
    直连?
    客户端实现基本原理-1
    客户端实现基本原理-2
    客户端实现基本原理-3 验证
    客户端实现基本原理-4 通知(发布订阅))

    客户端接入流程
    Jedis

    11 三个定时任务

    三个定时任务
    每 10s info
    第2个监控任务,每 2s 发布订阅
    第3个监控任务,每 1s PING

    12 主观下线和客观下线

    主观下线和客观下线

    13 领导者选举

    领导者选举
    选举实例

    14 故障转移

    领导者节点完成
    选择合适的slave节点

    15 常见开发运维问题-目录

    16 节点运维


    主节点
    节点下线
    节点上线

    17 高可用的读写分离

    看一下JedisSentinelPool的实现




    private HostAndPort initSentinels(Set<String> sentinels, final String masterName) {
    
        HostAndPort master = null;
        boolean sentinelAvailable = false;
    
        log.info("Trying to find master from available Sentinels...");
        // 遍历所有 Sentinel 节点
        for (String sentinel : sentinels) {
          final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
    
          log.fine("Connecting to Sentinel " + hap);
    
          Jedis jedis = null;
          try {
            // 找到一个可运行的,并用jedis连接上去
            jedis = new Jedis(hap.getHost(), hap.getPort());
    
            // 通过 mastername 区分 sentinel 节点,得到其地址
            List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName);
    
            // connected to sentinel...
            sentinelAvailable = true;
            //节点不可用继续遍历
            if (masterAddr == null || masterAddr.size() != 2) {
              log.warning("Can not get master addr, master name: " + masterName + ". Sentinel: " + hap
                  + ".");
              continue;
            }
    
            master = toHostAndPort(masterAddr);
            log.fine("Found Redis master at " + master);
            // 找到可用节点,结束遍历,跳出循环
            break;
          } catch (JedisException e) {
            // resolves #1036, it should handle JedisException there's another chance
            // of raising JedisDataException
            log.warning("Cannot get master address from sentinel running @ " + hap + ". Reason: " + e
                + ". Trying next one.");
          } finally {
            if (jedis != null) {
              jedis.close();
            }
          }
        }
        //无可用节点,抛异常
        if (master == null) {
          if (sentinelAvailable) {
            // can connect to sentinel, but master name seems to not
            // monitored
            throw new JedisException("Can connect to sentinel, but " + masterName
                + " seems to be not monitored...");
          } else {
            throw new JedisConnectionException("All sentinels down, cannot determine where is "
                + masterName + " master is running...");
          }
        }
    
        log.info("Redis master running at " + master + ", starting Sentinel listeners...");
        //当拿到master节点后,所要做的就是订阅那个消息,客户端只需订阅该频道即可
        for (String sentinel : sentinels) {
          final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
          MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
          // whether MasterListener threads are alive or not, process can be stopped
          masterListener.setDaemon(true);
          masterListeners.add(masterListener);
          masterListener.start();
        }
    

    以下为监听线程类

    protected class MasterListener extends Thread {
    
        protected String masterName;
        protected String host;
        protected int port;
        protected long subscribeRetryWaitTimeMillis = 5000;
        protected volatile Jedis j;
        protected AtomicBoolean running = new AtomicBoolean(false);
    
        protected MasterListener() {
        }
    
        public MasterListener(String masterName, String host, int port) {
          super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port));
          this.masterName = masterName;
          this.host = host;
          this.port = port;
        }
    
        public MasterListener(String masterName, String host, int port,
            long subscribeRetryWaitTimeMillis) {
          this(masterName, host, port);
          this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis;
        }
    
        @Override
        public void run() {
    
          running.set(true);
    
          while (running.get()) {
    
            j = new Jedis(host, port);
    
            try {
              // double check that it is not being shutdown
              if (!running.get()) {
                break;
              }
    
              j.subscribe(new JedisPubSub() {
                @Override
                public void onMessage(String channel, String message) {
                  log.fine("Sentinel " + host + ":" + port + " published: " + message + ".");
    
                  String[] switchMasterMsg = message.split(" ");
    
                  if (switchMasterMsg.length > 3) {
    
                    if (masterName.equals(switchMasterMsg[0])) {
                      initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4])));
                    } else {
                      log.fine("Ignoring message on +switch-master for master name "
                          + switchMasterMsg[0] + ", our master name is " + masterName);
                    }
    
                  } else {
                    log.severe("Invalid message received on Sentinel " + host + ":" + port
                        + " on channel +switch-master: " + message);
                  }
                }
              }, "+switch-master");
    
            } catch (JedisConnectionException e) {
    
              if (running.get()) {
                log.log(Level.SEVERE, "Lost connection to Sentinel at " + host + ":" + port
                    + ". Sleeping 5000ms and retrying.", e);
                try {
                  Thread.sleep(subscribeRetryWaitTimeMillis);
                } catch (InterruptedException e1) {
                  log.log(Level.SEVERE, "Sleep interrupted: ", e1);
                }
              } else {
                log.fine("Unsubscribing from Sentinel at " + host + ":" + port);
              }
            } finally {
              j.close();
            }
          }
        }
    
        public void shutdown() {
          try {
            log.fine("Shutting down listener on " + host + ":" + port);
            running.set(false);
            // This isn't good, the Jedis object is not thread safe
            if (j != null) {
              j.disconnect();
            }
          } catch (Exception e) {
            log.log(Level.SEVERE, "Caught exception while shutting down: ", e);
          }
        }
      }
    
    感知到主从切换时,接收消息并重新初始化连接池

    从节点的作用

    由于Redis Sentinel只会对主节点进行故障转移,对从节点采取主观的下线,所以需要自定义一个客户端来监控对应的事件


    三个消息
    高可用读写分离

    18 总结



    相关文章

      网友评论

        本文标题:Redis实践(八)-Sentinal

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