Java实现Redis哨兵的示例代码

前言:

本文将采用文字+代码的方式,讲解redis版哨兵的实现,所有代码都将写在一个类中,每个属性和方法都会结合文字加以说明。

1. 哨兵(Sentinel)主要功能如下:

1、不时的监控redis节点是否良好运行,如果节点不可达就会对节点进行下线标识

2、如果被标识的是主节点,哨兵就会选举一个redis从(slave)节点成为新的主节点继续对外提供读写服务, 进而实现自动故障转移,保证系统的高可用。

3、在redis主节点 和 从节点 进行切换后,主节点配置文件master_redis.conf、从节点配置文件slave_redis.conf都要发生改变。

2. 准备工作:

  • Redis集群推荐一主两从,共三个节点。
  • jedis-2.9.0.jar 客户端框架

3. 代码实现

JavaSentinel.java

package com.middleware.redis.sentinels;

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

import java.util.*;

/**
 * java版哨兵
 * 
 * @author 93733
 *
 */
public class JavaSentinel {

    // 主节点ip:端口    127.0.0.1:6379
    static String masterAddress = "127.0.0.1:6379";
    // 所有 slave
    static final Vector<String> slaveRedisServers = new Vector<String>();
    // 坏掉的实例
    static final Vector<String> badRedisServers = new Vector<String>();

    // 连接池对象
    static JedisPool jedisPool ;

    // 连接池配置信息对象
    private static JedisPoolConfig config = new JedisPoolConfig();

    /**
     * 配置连接池信息
     * @return
     */
    static {

        // 最大连接数10
        config.setMaxTotal(10);
        //最大空闲连接数5
        config.setMaxIdle(5);

    }

    /**
     * 获取jedis 实例
     * @param
     * @return
     */
    public Jedis newJedisInstance() {
        return jedisPool.getResource() ;
    }

    volatile static JavaSentinel javaSentinel;

    /**
     * 创建JavaSentinel对象
     * @param isOpenSentinel 是否开启哨兵 true 开启, false 不开启
     * @return
     *
     * 1) 如果开启哨兵, 我们创建一个定时任务, 延迟1秒,间隔3秒执行一次
     * 2)每次执行时, 任务如下:
     *              // 检测 master是否可以
     *                 checkMaster();
     *                 // 更新slave列表
     *                 updateSlaves();
     *                 // 检测坏掉的实例是否恢复正常
     *                 checkBadServer();
     *
     * 3)初始化 jedisPool 对象 和 javaSentinel对象
     *
     */

    public static synchronized JavaSentinel getInstance(boolean isOpenSentinel){

        // 是否开启java哨兵
        if(isOpenSentinel){

            // 定时任务
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    // 检测 master是否可以
                    checkMaster();
                    // 更新slave列表
                    updateSlaves();
                    // 检测坏掉的实例是否恢复正常
                    checkBadServer();

                }
            }, 1000, 3000);
        }

        if(null == javaSentinel){

            /**
             * 初始化redis连接池对象
             */
            String[] serverInfo = masterAddress.split(":");
            String masterHost = serverInfo[0] ;
            int masterPort = Integer.parseInt(serverInfo[1]) ;
            jedisPool = new JedisPool(config, masterHost, masterPort, 100000);

            //初始化当前类对象
            javaSentinel = new JavaSentinel();
        }

        return javaSentinel;

    }

    /**
     * 该方法通过ping 方式, 检验当前redis主节点是否在线
     *
     * 如若发生异常, 则主节点挂掉, 需要做如下两步:
     * 1)如果捕获到了异常证明:  redis节点挂掉, 我们需要将当前主节点address保存到badRedisServers集合中
     * 2)调用changeMaster() 方法,选举从节点作为新的主
     */
    private static void checkMaster() {
        // 主从切换
        // 检查状态
        System.out.println("检查master状态:" + masterAddress);
        String masterHost = masterAddress.split(":")[0];
        int masterPort = Integer.parseInt(masterAddress.split(":")[1]);
        try {
            Jedis jedis = new Jedis(masterHost, masterPort);
            jedis.ping();
            jedis.close();
        } catch (Exception e) {
            // master挂掉啦
            badRedisServers.add(masterAddress);
            // 切换master
            changeMaster();
        }
    }

    /**
     * 切换master
     *
     * 1) 从slaveRedisServers集合中, 获取一个从节点地址
     * 2)通过地址创建jedis对象尝试ping动作,验证器是否在线
     * 3)没发生异常,证明在线,我们需要禁用它从死掉master继续同步数据
     * 4)修改属性masterAddress 为新选举出来的slave地址
     * 5)如果发生异常,则将当前slave存放在badRedisServers集合中, 进入下一次循环重试1-4 动作
     * 6)选举成功后,将当前slave从 slaveRedisServers集合中移除掉
     *
     * 7)遍历slaveRedisServers集合,将其他从节点 主从复制配置更新到刚刚选举出来的新主节点身上
     */
    private static void changeMaster() {
        Iterator<String> iterator = slaveRedisServers.iterator();
        while (iterator.hasNext()) {
            String slaveAddress = iterator.next();
            try {
                String slaveHost = slaveAddress.split(":")[0];
                int slavePort = Integer.parseInt(slaveAddress.split(":")[1]);
                Jedis jedis = new Jedis(slaveHost, slavePort);

                /*确保当前从节点在线*/
                jedis.ping();

                /*禁用当前从节点同步复制*/
                jedis.slaveofNoOne();
                jedis.close();
                masterAddress = slaveAddress;
                System.out.println("产生新的master:" + masterAddress);
                break;
            } catch (Exception e) {
                badRedisServers.add(slaveAddress);
            } finally {
                iterator.remove();
            }
        }

        // 所有slave切到新的master
        for (String slave : slaveRedisServers) {
            String slaveHost = slave.split(":")[0];
            int slavePort = Integer.parseInt(slave.split(":")[1]);
            Jedis jedis = new Jedis(slaveHost, slavePort);
            jedis.slaveof(masterAddress.split(":")[0], Integer.parseInt(masterAddress.split(":")[1]));
            jedis.close();
        }
    }

    /**
     * 更新当前所有从节点到 slaveRedisServers中
     *
     * 1)根据masterAddress 创建主节点Jedis对象
     * 2)获取主节点replication配置信息jedis.info("replication");
     * 3)根据配置信息, 获取到当前主节点从节点个数
     * 4)循环遍历从节点个数, 如果个数大于0, 则清空当前 slaveRedisServers集合
     * 5)从配置信息中截取出所有从节点的ip:端口后,放入到 slaveRedisServers集合中
     *
     */
    private static void updateSlaves() {
        // 获取所有slave
        try {
            String masterHost = masterAddress.split(":")[0];
            int masterPort = Integer.parseInt(masterAddress.split(":")[1]);
            Jedis jedis = new Jedis(masterHost, masterPort);
            String info_replication = jedis.info("replication");
            // 解析info replication
            String[] lines = info_replication.split("\r\n");
            int slaveCount = Integer.parseInt(lines[2].split(":")[1]);
            if (slaveCount > 0) {
                slaveRedisServers.clear();
                for (int i = 0; i < slaveCount; i++) {
                    String host = lines[3 + i].split(",")[0].split("=")[1];
                    String port = lines[3 + i].split(",")[1].split("=")[1];
                    slaveRedisServers.add(host + ":" + port);
                }
            }
            System.out.println("更新slave列表:" + Arrays.toString(slaveRedisServers.toArray(new String[] {})));
            jedis.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("更新slave失败:" + e.getMessage());
        }
    }

    /**
     * 检测坏掉的实例是否恢复正常
     * 1)如果调用 pint() 没有发生异常, 证明恢复正常
     * 2)恢复正常后,先将当前节点主从复制的配置通过slaveof() 挂载当前节点上
     * 3)将当前节点地址从 badRedisServers集合中remove()掉, 并添加到 slaveRedisServers集合中。
     * 
     */
    private static void checkBadServer() {
        // 获取所有slave
        Iterator<String> iterator = badRedisServers.iterator();
        while (iterator.hasNext()) {
            String bad = iterator.next();
            try {
                String badHost = bad.split(":")[0];
                int badPort = Integer.parseInt(bad.split(":")[1]);
                Jedis badServer = new Jedis(badHost, badPort);
                badServer.ping();

                // 如果ping没有问题,则挂在当前的master
                badServer.slaveof(masterAddress.split(":")[0], Integer.parseInt(masterAddress.split(":")[1]));
                badServer.close();

                slaveRedisServers.add(bad);
                iterator.remove();
                System.out.println(bad + " 恢复正常,当前master:" + masterAddress);
            } catch (Exception e) {
            }
        }
    }
}

到此这篇关于Java实现Redis哨兵的示例代码的文章就介绍到这了,更多相关Java Redis哨兵内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java Redis配置Redisson的方法详解

    目录 需要的Maven application-redis.yml Session共享配置 Redisson配置 其他Redisson的Config配置方式 需要的Maven <!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <e

  • Java Redis Template批量查询指定键值对的实现

    目录 一.Redis使用pipeline批量查询所有键值对 二.批量获取指定的键值对列表 一.Redis使用pipeline批量查询所有键值对 一次性获取所有键值对的方式: private RedisTemplate redisTemplate; @SuppressWarnings({ "rawtypes", "unchecked" })     public List executePipelined(Collection<String> keySet

  • Java redis使用场景介绍

    目录 1.作为缓存 1.1 为何使用 1.2 什么样的数据适合放入缓存 1.3 使用redis作为缓存 1.3.1 未使用配置类 1.3.2 使用配置类 2.分布式锁 2.1 压测工具的使用 2.2 库存项目 2.2.1 controller层 2.2.2 dao层 2.2.3 entity层 2.2.4 service层 2.2.5 mapper 2.2.6 依赖 2.2.7 测试结果 2.3 解决方案 2.3.1 使用 synchronized 或者lock锁 2.3.2 使用redisTe

  • Java与SpringBoot对redis的使用方式

    目录 1.Java连接redis 1.1 使用Jedis 1.2 使用连接池连接redis 1.3 java连接redis集群模式 2.SpringBoot整合redis 2.1 StringRedisTemplate 2.2 RedisTemplate 1.Java连接redis redis支持哪些语言可以操作 (去redis官网查询) 1.1 使用Jedis  (1)添加jedis依赖 <dependency> <groupId>junit</groupId> &l

  • Java连接Redis全过程讲解

    目录 Java连接Redis 引入jar包 编写测试类 Jedis常用方法API 一.首先把 jedis-2.1.0.jar(jedis基础包) 二.创建 jedis对象 三.键操作 四.字符串操作 五.整数和浮点数操作 六.列表(List)操作 七.集合(Set)操作 八.哈希(Hash)操作 九.有序集合(Zsort)操作 十.排序操作 Java连接Redis Jedis Client是Redis官网推荐的一个面向java客户端,库文件实现了对redis各类API进行封装调用. 引入jar包

  • Java Redis Redisson配置教程详解

    目录 需要的Maven application-redis.yml Session共享配置 其他Redisson的Config配置方式 Redisson的Config(单机版配置) Redisson的Config(哨兵版配置) Redisson的Config(主从版配置) Redisson的Config(集群模式) Redisson的Config(红锁模式) 需要的Maven <!--redis--> <dependency> <groupId>org.springfr

  • Java实现redis分布式锁的三种方式

    目录 一.引入原因 二.分布式锁实现过程中的问题 问题一:异常导致锁没有释放 问题二:获取锁与设置过期时间操作不是原子性的 问题三:锁过期之后被别的线程重新获取与释放 问题四:锁的释放不是原子性的 问题五:其他的问题? 三.具体实现 1. RedisTemplate 2. RedisLockRegistry 3. 使用redisson实现分布式锁 一.引入原因 在分布式服务中,常常有如定时任务.库存更新这样的场景. 在定时任务中,如果不使用quartz这样的分布式定时工具,只是简单的使用定时器来

  • Java-Redis-Redisson分布式锁的功能使用及实现

    目录 前置 基础设施 功能使用和介绍 其他悲观锁的实现方式 前置 Java-Redis-Redisson配置基础上我们进行了改造,让锁的使用更加方便 基础设施 RedissonLock import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Targ

  • Java利用redis zset实现延时任务详解

    目录 一.实现原理 二.准备工作 三.代码实现 四.优缺点 所谓的延时任务给大家举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消.「订单30分钟不付款自动取消,这个任务就是一个延时任务.」   我之前已经写过2篇关于延时任务的文章: <通过DelayQueue实现延时任务> <基于netty时间轮算法实战> 这两种方法都有一个缺点:都是基于单体应用的内存的方式运行延时任务的,一旦出现单点故障,可能出现延时任务数据的丢失.所以此篇文章给大家介绍实现延时任务的第

  • Java实现Redis哨兵的示例代码

    前言: 本文将采用文字+代码的方式,讲解redis版哨兵的实现,所有代码都将写在一个类中,每个属性和方法都会结合文字加以说明. 1. 哨兵(Sentinel)主要功能如下: 1.不时的监控redis节点是否良好运行,如果节点不可达就会对节点进行下线标识 2.如果被标识的是主节点,哨兵就会选举一个redis从(slave)节点成为新的主节点继续对外提供读写服务, 进而实现自动故障转移,保证系统的高可用. 3.在redis主节点 和 从节点 进行切换后,主节点配置文件master_redis.con

  • Java连接postgresql数据库的示例代码

    本文介绍了Java连接postgresql数据库的示例代码,分享给大家,具体如下: 1.下载驱动jar 下载地址:https://jdbc.postgresql.org/download.html 2.导入jar包 新建lib文件夹,将下载的jar驱动包拖到文件夹中. 将jar驱动包添加到Libraries 3.程序代码如下:HelloWorld.java package test; import java.sql.Connection; import java.sql.DriverManage

  • java 生成文字图片的示例代码

    本文主要介绍了java 生成文字图片的示例代码,分享给大家,具体如下: import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO;

  • Java动态规划之编辑距离问题示例代码

    动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移.一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划. 动态规划实际上是一类题目的总称,并不是指某个固定的算法.动态规划的意义就是通过采用递推(或者分而治之)的策略,通过解决大问题的子问题从而解决整体的做法.动态规划的核心思想是巧妙的将问题拆分成多个子问题,通过计算子问题而得到整体问题的解.而子问题又可以拆分成更多的子问题,从而用类似递推迭代的方法解决要求的问题.问题描述: 对于序列S和T,

  • Java的静态类型检查示例代码详解

    关于静态类型检查和动态类型检查的解释: 静态类型检查:基于程序的源代码来验证类型安全的过程: 动态类型检查:在程序运行期间验证类型安全的过程: Java使用静态类型检查在编译期间分析程序,确保没有类型错误.基本的思想是不要让类型错误在运行期间发生. 在各色各样的编程语言中,总共存在着两个类型检查机制:静态类型检查和动态类型检查. 静态类型检查是指通过对应用程序的源码进行分析,在编译期间就保证程序的类型安全. 动态类型检查是在程序的运行过程中,验证程序的类型安全.在Java中,编译期间使用静态类型

  • Java随机生成身份证完整示例代码

    身份证算法实现 1.号码的结构 公民身份号码是特征组合码, 由十七位数字本体码和一位校验码组成. 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码  三位数字顺序码和一位数字校验码. 2.地址码(前六位数) 表示编码对象常住户口所在县(市.旗.区)的行政区划代码,按GB/T2260的规定执行. 3.出生日期码(第七位至十四位) 表示编码对象出生的年.月.日,按GB/T7408的规定执行,年.月.日代码之间不用分隔符. 4.顺序码(第十五位至十七位) 表示在同一地址码所标识的区域范围内,

  • java实现基因序列比较的示例代码

    设计算法,计算两给定基因序列的相似程度. 人类基因由4种核苷酸,分别用字母ACTG表示.要求编写一个程序,按以下规则比较两个基因序列并确定它们的相似程度.即给出两个基因序列AGTGATG和GTTAG,它们有多相似呢?测量两个基因相似度的一种方法称为对齐.使用对齐方法可以在基因的适当位置加入空格,让两个基因的长度相等,然后根据基因的分值矩阵计算分数. 看了很多代码基本上都是用c++或者c写的,但是习惯性写java就用java实现一下 基本的思路就是,和背包问题差不多,实现还是模仿填表的形式去实现的

  • 用java实现跳动的小球示例代码

    实现效果为一个小球接触左右侧时,会反向的运动. import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.paint.Colo

  • 在Java中操作Zookeeper的示例代码详解

    依赖 <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.6.0</version> </dependency> 连接到zkServer //连接字符串,zkServer的ip.port,如果是集群逗号分隔 String connectStr = "192.

  • Java 静态数据初始化的示例代码

    无论创建多少个对象,静态数据都只占用一份存储区域.static关键字不能应用于局部变量,因此它只能作用于域.如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初始值:如果它是一个对象引用,那么它的默认初始值就是null class Bowl { public Bowl(int marker) { System.out.println("Bowl(" + marker + ")"); } void f1(int marker) { Sy

随机推荐