Jedis哨兵模式如何实现主从的读写分离

前言:redis的主从和哨兵模式,在官方的功能模式下,从实例只有数据备份和作为主实例的备机作用,并不具备我们想象中的主实例负责写,从实例负责读的职责分工

实际项目中不会使用单独的主从复制的模式的吧,哨兵模式还有使用的项目,那哨兵模式下如何实现读写分离呢?

废话不多说直接上demo,顺便看下原生的jedis哨兵是不是真的不支持读写分离,这可不是我在瞎说:

jedis版本:

<dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.10.2</version>
</dependency>

 我们使用jedis的JedisSentinelPool类来创建哨兵链接

Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.84.1:16379");
sentinels.add("192.168.84.1:16380");
sentinels.add("192.168.84.1:16381");
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels);
// 永远返回的都是 Master 连接
Jedis master = pool.getResource();

 看下getResource()源码:

public Jedis getResource() {
        while(true) {
            Jedis jedis = (Jedis)super.getResource();
            jedis.setDataSource(this);
            HostAndPort master = this.currentHostMaster;
            HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient().getPort());
           // 是主实例的连接信息,才会返回jedis连接
            if (master.equals(connection)) {
                return jedis;
            }

            this.returnBrokenResource(jedis);
        }
    }

没骗你吧?

那如果我们想实现jedis哨兵模式的读写分离,要怎么做呢 ,来,直接上硬货:

package com.cjian.jedis;


import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: cjian
 * @Date: 2023/7/13 13:49
 * @Des:
 */
public class SentinelDemo {
    public static void main(String[] args) {
        Set<String> sentinels = new HashSet<>();
        sentinels.add("192.168.84.1:16379");
        sentinels.add("192.168.84.1:16380");
        sentinels.add("192.168.84.1:16381");
        JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels);
        // 永远返回的都是 Master 连接
        Jedis master = pool.getResource();

        // 获取 Master 的地址信息
        HostAndPort hostAndPort = pool.getCurrentHostMaster();
        System.out.println("master = " + hostAndPort.getHost() + ":" + hostAndPort.getPort() + " " + master);

        testRWSeparation(master);
    }

    private static void testRWSeparation(Jedis master){
        // 构造我们自己的对象
        MyJedis myJedis = new MyJedis();
        myJedis.setMaster(master);

        // 解析主从信息,提取从节点信息
        Pattern pattern = Pattern.compile("^slave\\d+:ip=(.+),port=(\\d+),state=.+$");
        String[] infos = master.info("replication").split("(\\r\\n)|(\\n)");
        for (String info : infos) {
            Matcher matcher = pattern.matcher(info);
            if (matcher.find()) {
                Jedis slave = new Jedis(matcher.group(1), Integer.valueOf(matcher.group(2)));
                myJedis.addSlaves(slave);
            }
        }

        // 写入数据
        myJedis.set("name", "cjian");

        // 读取数据
        String result = myJedis.get("name");
        System.out.println("result = " + result);

        result = myJedis.get("name");
        System.out.println("result = " + result);
    }


    static class MyJedis {
        /**
         * 主节点
         */
        private Jedis master;
        /**
         * 从节点,可能会有多个
         */
        private List<Jedis> slaves = new ArrayList<>();

        public void setMaster(Jedis master) {
            this.master = master;
        }

        public void addSlaves(Jedis slave) {
            this.slaves.add(slave);
        }

        public String get(String key) {
            Jedis jedis = slaves.get((int) (Math.random() * slaves.size()));
            System.out.println(">> get:" + jedis);
            return jedis.get(key);
        }

        public void set(String key, String value) {
            master.set(key, value);
        }
    }
}

输出如下: 

14:36:09.426 [main] INFO  r.clients.jedis.JedisSentinelPool - Trying to find master from available Sentinels...
14:36:09.437 [main] DEBUG r.clients.jedis.JedisSentinelPool - Connecting to Sentinel 192.168.84.1:16379
14:36:09.557 [main] DEBUG r.clients.jedis.JedisSentinelPool - Found Redis master at 192.168.84.1:6381
14:36:09.558 [main] INFO  r.clients.jedis.JedisSentinelPool - Redis master running at 192.168.84.1:6381, starting Sentinel listeners...
14:36:09.765 [main] INFO  r.clients.jedis.JedisSentinelPool - Created JedisPool to master at 192.168.84.1:6381
master = 192.168.84.1:6381 redis.clients.jedis.Jedis@783e6358
>> get:redis.clients.jedis.Jedis@735f7ae5
result = cjian
>> get:redis.clients.jedis.Jedis@180bc464
result = cjian

Spring Boot整合Redis主从哨兵模式主要用于提供高可用的Redis服务。哨兵模式是Redis的高可用解决方案,它是由一个或多个哨兵进程组成的系统,可以监控所有的Redis主从服务器,并在主服务器出现故障时自动进行故障转移。整合步骤大致如下: 1. 添加Spring Boot与Redis依赖:在项目的`pom.xml`中添加Spring Boot的Redis模块依赖,以及Jedis客户端依赖(或者其他支持哨兵模式的Redis客户端库)。 2. 配置Redis属性:在`application.properties`或`application.yml`中配置Redis的连接信息,包括哨兵的地址、端口、主节点名称、密码等。 3. 创建Redis配置类:使用`@Configuration`注解创建一个配置类,在类中配置`StringRedisTemplate`或`RedisTemplate`,并设置序列化方式,使用哨兵工厂`RedisConnectionFactory`来创建连接。 4. 配置哨兵工厂:在配置类中注入`SentinelConfiguration`,配置哨兵的相关信息,如主节点名称(master name)、哨兵节点列表等。 5. 使用RedisTemplate操作Redis:在需要使用Redis的地方注入`StringRedisTemplate`或`RedisTemplate`,进行数据的读写操作。 6. 启动Spring Boot应用:通过Spring Boot的自动配置和上面的配置,启动应用后,Spring Data Redis会自动连接到哨兵,并根据哨兵的指示连接到主节点或从节点。 通过上述步骤,Spring Boot应用即可实现与Redis的整合,并通过哨兵模式保障Redis服务的高可用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值