Redis 读写分离高可用方案

Redis 读写分离高可用方案

原理

利用redis 的主从复制,哨兵机制实现读写分离和高可用
1、redis 主从复制:redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。
2、Redis 哨兵机制三大功能
监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
提醒(Notification): 当被监控的某个Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover): 当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master

实现步骤

1、配置redis主从复制
1、准备两台服务器(192.168.138.128、192.168.138.129)分别安装上redis,(可用yum install -y redis),以128服务器为主服务器,redis的主节点,129服务器为redis的从服务器。

2、修改129从节点的redis配置文件,在配置文件中添加两项

(若是用yum源安装的redis ,配置文件默认在/etc 路径下, vim /etc/redis.conf)

slaveof 192.168.138.128 6379(主服务器的ip和端口)
masterauth 123456 (主服务器redis 密码,没有可以不写)

3、主从服务器都需求修改远程访问 (vim /etc/redis.conf)

bind 0.0.0.0(允许所有ip访问)
protected-mode no(放开远程访问)
在这里插入图片描述
在这里插入图片描述
4、主从服务器开放防火墙端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent #开启端口
firewall-cmd --reload #重启firewall

5、启动启动两台服务器的redis (systemctl status redis命令启动),用redis Manager 远程工具连接,并在主服务器redis(128)上添加一个值,在从服务器的redis可以查看到一个同样的值,并且在从服务器redis上无法添加值,则主从服务配置成功!如图所示
在这里插入图片描述
在这里插入图片描述
6、进入redis客户端用info replication 命令查看redis 角色
在这里插入图片描述
在这里插入图片描述
2、哨兵模式配置(高可用)
1、在主从服务器上修改sentinel.conf 哨兵配置文件(默认在/etc/sentinel.conf),找不到可以使用find / -name "sentinel*"命令进行查找
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、修改完成之后启动哨兵模式,/usr/bin/redis-server /etc/sentinel.conf --sentinel &(/usr/bin/redis-server 为redis服务端启动文件)
在这里插入图片描述

用ps -ef|grep redis 查看sentinel是否启动
在这里插入图片描述

3、验证哨兵是否生效
首先将主服务器的redis 的进程杀死
在这里插入图片描述

在从服务器的redis客户端用info replication 命令查看redis 角色,这时从服务器的redis已经提升为master

在这里插入图片描述

java应用

文件目录

// An highlighted block
package com.redis.temp.kaiwen.conf;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


/**
 * The type Jedis pool factory.
 *
 * @author ljy
 * @version 1.0
 * @date 2019 -12-12 14:31:01
 */
@Configuration
public class JedisPoolFactory {

    //master
    @Value("${master.spring.redis.host}")
    private String masterHost;

    @Value("${master.spring.redis.port}")
    private int masterPort;

    @Value("${master.spring.redis.password}")
    private String masterPassword;

    @Value("${master.spring.redis.database}")
    private int masterDatabase;

    //node
    @Value("${node.spring.redis.host}")
    private String nodeHost;

    @Value("${node.spring.redis.port}")
    private int nodePort;

    @Value("${node.spring.redis.password}")
    private String nodePassword;

    @Value("${node.spring.redis.database}")
    private int nodeDatabase;

    private static final int MAX_IDLE = 200; //最大空闲连接数
    private static final int MIN_IDLE = 50; //最大空闲连接数
    private static final int MAX_TOTAL = 1024; //最大连接数
    private static final long MAX_WAIT_MILLIS = 10000; //建立连接最长等待时
    
    /**
     * Gets jedis pool.
     *
     * @return the jedis pool
     */
    @Bean(name = "masterPool")
    public JedisPool getMasterJedisPool() {
        return getJedisPool(masterHost, masterPort, masterPassword, masterDatabase);
    }


    @Bean(name = "nodePool")
    public JedisPool getNodeJedisPool() {
        return getJedisPool(nodeHost, nodePort, nodePassword, nodeDatabase);
    }

    private JedisPool getJedisPool(String nodeHost, int nodePort, String nodePassword, int nodeDatabase) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(MAX_IDLE);
        config.setMinIdle(MIN_IDLE);
        config.setMaxTotal(MAX_TOTAL);
        config.setMaxWaitMillis(MAX_WAIT_MILLIS);
        config.setMinEvictableIdleTimeMillis(60000);
        config.setNumTestsPerEvictionRun(-1);
        config.setTestOnReturn(true);
        return new JedisPool(config, nodeHost, nodePort, 1000, nodePassword, nodeDatabase);
    }


}

package com.redis.temp.kaiwen.redis;

import io.netty.channel.ChannelHandler.Sharable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;

/**
 * @author: liangjinyin
 * @Date: 2020-11-06
 * @Description: redis 操作工具类
 */
@Component
@Sharable
public class Master {
    @Resource(name = "masterPool")
    private JedisPool jedisPool;

    /**
     * 获取String值,获取不到返回默认值
     *
     * @param key   the key
     * @param value the value
     * @return value or default
     */
    public String getStringValueOrDefault(String key, String value) {
        if (StringUtils.isEmpty(key)) {
            return value;
        }
        if (jedisPool == null) {
            return value;
        }
        try (Jedis jedis = jedisPool.getResource()) {
            String s = jedis.get(key);
            return StringUtils.isEmpty(s) ? value : s;
        } catch (Exception e) {
            e.printStackTrace();
            return value;
        }
    }
}

package com.redis.temp.kaiwen.redis;

import io.netty.channel.ChannelHandler.Sharable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;

/**
 * @author: liangjinyin
 * @Date: 2020-11-06
 * @Description: redis 操作工具类
 */
@Component
@Sharable
public class Node {

    @Resource(name = "nodePool")
    private JedisPool jedisPool;

    /**
     * 获取String值,获取不到返回默认值
     *
     * @param key   the key
     * @param value the value
     * @return value or default
     */
    public String getStringValueOrDefault(String key, String value) {
        if (StringUtils.isEmpty(key)) {
            return value;
        }
        if (jedisPool == null) {
            return value;
        }
        try (Jedis jedis = jedisPool.getResource()) {
            String s = jedis.get(key);
            return StringUtils.isEmpty(s) ? value : s;
        } catch (Exception e) {
            e.printStackTrace();
            return value;
        }
    }
}

package com.redis.temp.kaiwen.redis;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @author: liangjinyin
 * @Date: 2020-11-06
 * @Description: redis 工具厂
 */
@Component
public class RedisUtils {

    @Resource
    private Master master;

    @Resource
    private Node node;


    public Master getMaster() {
        return master;
    }

    public Node getNode() {
        return node;
    }
}

package com.redis.temp.kaiwen.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author: liangjinyin
 * @Date: 2020-11-06
 * @Description:
 */
@RestController
@RequestMapping("/redis")
@Slf4j
public class RedisTestController {

    @Resource
    private RedisUtils redisUtils;

    @GetMapping("/aa")
    public String getTest() {
        String master = redisUtils.getMaster().getStringValueOrDefault("ff", "ee");
        String node = redisUtils.getNode().getStringValueOrDefault("aa", "ee");
        log.info("redis主节点获取值{}, redis 次节点获取值{}", master, node);
        return master + node;
    }
}

参考文献
[1]: https://blog.csdn.net/qq_40742298/article/details/104255152?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight
[2]: https://www.jianshu.com/p/7d5fbf90bcd7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值