继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Redis实践(八)-Sentinal

呼如林
关注TA
已关注
手记 342
粉丝 101
获赞 363

1

5bcc31da00015f7410000351.jpg

目录

2 主从复制高可用?

5bcc31dc00019bcf10000517.jpg


5bcc31dd00015f5a10000450.jpg

故障出现主节点挂掉


5bcc31de00015ca110000392.jpg

主从复制-mster宕掉故障处理

3  Redis Sentinel 架构

5bcc31df000173d810000642.jpg


5bcc31e10001de8410000518.jpg


5bcc31e20001bc1210000548.jpg

可监控多套

4 安装与配置

5bcc31e30001c99310000281.jpg

安装与配置


5bcc31e400018c0010000639.jpg


5bcc31e500017c1a10000406.jpg

Redis 主节点


5bcc31e90001cfac10000441.jpg

Redis 从节点


5bcc31ea0001b66210000340.jpg

Sentinel 主要配置

5 安装与演示

5bcc31eb0001aa6d07450088.jpg


5bcc31ec0001a3bd08150247.jpg

主节点配置


5bcc31ed0001109a10000234.jpg

快速生成从节点配置文件


5bcc31ee0001120010000234.jpg

重定向


5bcc31f00001feeb07530914.jpg

打印检查配置文件


5bcc31f1000124c210000625.jpg

启动

6 客户端

5bcc31f20001fb2910000393.jpg

客户端


5bcc31f30001087210000362.jpg

直连?


1000

客户端实现基本原理-1


1000

客户端实现基本原理-2


1000

客户端实现基本原理-3 验证


1000

客户端实现基本原理-4 通知(发布订阅))


1000


1000

客户端接入流程


1000

Jedis

11 三个定时任务

917

三个定时任务


1000

每 10s info


950

第2个监控任务,每 2s 发布订阅


982

第3个监控任务,每 1s PING

12 主观下线和客观下线

943

主观下线和客观下线

13 领导者选举

998

领导者选举


995

选举实例

14 故障转移

1000

领导者节点完成


983

选择合适的slave节点

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

1000

16 节点运维

984


972

主节点


972

节点下线


980

节点上线

17 高可用的读写分离

看一下JedisSentinelPool的实现

1000


1000


1000


1000


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);
      }
    }
  }

1000

感知到主从切换时,接收消息并重新初始化连接池


1000


977

从节点的作用


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


962

三个消息


782

高可用读写分离

18 总结

1000


1000


1000



作者:芥末无疆sss
链接:https://www.jianshu.com/p/6dad9cc2323e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP