Redis实践(八)-Sentinal12 主从复制高可用?3 Redis Sentinel 架构4 安装与配置5 安装与演示6 客户端11 三个定时任务12 主观下线和客观下线13 领导者选举14

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 总结

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WindCoder

微信小程序踩坑记-Java基于SSM下的post请求

最近在持续踩微信小程序的坑,canvas和WebSocket的暂时还没找到相关的解决方案,暂时先将post请求无法获取data参数的坑填上。直接附上解决方案,已...

1.1K1
来自专栏智能大石头

线程池ThreadPool及Task调度机制分析

近1年,偶尔发生应用系统启动时某些操作超时的问题,特别在使用4核心Surface以后。笔记本和台式机比较少遇到,服务器则基本上没有遇到过。

1220
来自专栏JAVA高级架构

Netty原理浅析

Netty是JBoss出品的高效的Java NIO开发框架。本文将主要分析Netty实现方面的东西,由于精力有限,本人并没有对其源码做了极细致的研 究。如果下面...

1172
来自专栏郭霖

巧用Android网络通信技术,在网络上直接传输对象

要做一个优秀的Android应用,使用到网络通信技术是必不可少的,很难想象一款没有网络交互的软件最终能发展得多成功。那么我们来看一下,一般Android应用程序...

2266
来自专栏Kubernetes

深度解析Kubernetes Local Persistent Volume(二)

摘要:上一篇博客”深度解析Kubernetes Local Persistent Volume(一)“对local volume的基本原理和注意事项进行了分析,...

1.6K3
来自专栏纯洁的微笑

springboot(五):spring data jpa的使用

在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data ...

7339
来自专栏架构师之旅

《Spring敲门砖之基础教程第一季》 第二章(1) Spring框架之IOC首例-HelloWorld

回顾 上一章我们主要学习了Spring的一些理论知识,对Spring框架有了一个总体的概括,大家应该在头脑里形成一个初步的印象,接下来我们就会针对Spring框...

20310
来自专栏Java架构师学习

浅析Netty

Netty是JBoss出品的高效的Java NIO开发框架,关于其使用,可参考我的另一篇文章 netty使用初步。本文将主要分析Netty实现方面的东西,由于精...

1551
来自专栏kl的专栏

spring batch进阶-基于RabbitMQ远程分区Step

关于spring batch概念及基本使用,可移步《spring batch精选,一文吃透spring batch》,本文主要内容为spring batch的进...

6827
来自专栏Java编程技术

Netty学习笔记(一)

Netty是一种可以轻松快速的开发类似协议服务器和客户端网络应用程序的NIO客户端服务器框架,它大大简化了TCP或者UDP服务器的网络编程,但是你仍然可以访问和...

1752

扫码关注云+社区

领取腾讯云代金券