SpringBoot Redis 哨兵配置(一主两从三哨兵配置)

SpringBoot Redis 哨兵配置(一主两从三哨兵 )配置

背景

Redis 哨兵模式作为Redis 的一种高可用方案,实现了主从复制、监控,故障转移等工作,在一定程度上保证了Redis的高可用,避免了因Redis服务宕机导致缓存服务不可用情况;本博文主要是基于Spring Boot进行搭建的Redis Sentinel(哨兵模式),采用的是 一主两从三哨兵

Redis Sentinel(一主两从三哨兵)配置

  1. Redis 服务列表:
    127.0.0.1:6379 (主服务器master)
    127.0.0.1:6380 (从服务器slave1)
    127.0.0.1:6381 (从服务器slave2)
    127.0.0.1:26379(哨兵1 sentinel1)
    127.0.0.1:36379 (哨兵2 sentinel2)
    127.0.0.1:46379 (哨兵3 sentinel3)
  2. 拷贝redis文件夹下的redis.windows.conf文件,更名为slave1.conf,slave2.conf两个配置文件,并进行修改配置,配置如下:

slave1.conf 配置

port 6380
#允许所有的ip进行访问
bind 0.0.0.0
#指定Redis主服务器(master) 的ip及端口号
slaveof 127.0.0.1 6379

slave2.conf 配置

port 6381
bind 0.0.0.0
#指定Redis 主从服务器(master)的ip及端口号
slaveof 127.0.0.1 6379

3.创建哨兵配置文件,配置文件分别为sentinel1.conf ,sentinel2.conf,sentinel3.conf
sentinel1配置

bind 0.0.0.0
#哨兵的端口
port 26379
# redis文件夹名称
dir "C:\\redis-master"
#参数解读: master 是主服务的名称,127.0.0.1 主服务ip 6379 主服务端口号 2是类似权重,当两个哨兵认为主服务宕机时,进行从新选举新的主服务;
sentinel monitor master 127.0.0.1 6379 2
#注意含有mymaster的配置,都必须放置在sentinel monitor master 127.0.0.1 6379 2之后,否则会出现问题

sentinel2配置

bind 0.0.0.0
#哨兵的端口
port 36379
# redis文件夹名称
dir "C:\\redis-master"
#参数解读: master 是主服务的名称,127.0.0.1 主服务ip 6379 主服务端口号 2是类似权重,当两个哨兵认为主服务宕机时,进行从新选举新的主服务;
sentinel monitor master 127.0.0.1 6379 2
#注意含有mymaster的配置,都必须放置在sentinel monitor master 127.0.0.1 6379 2之后,否则会出现问题

sentinel3配置

bind 0.0.0.0
#哨兵的端口
port 46379
# redis文件夹名称
dir "C:\\redis-master"
#参数解读: master 是主服务的名称,127.0.0.1 主服务ip 6379 主服务端口号 2是类似权重,当两个哨兵认为主服务宕机时,进行从新选举新的主服务;
sentinel monitor master 127.0.0.1 6379 2
#注意含有mymaster的配置,都必须放置在sentinel monitor master 127.0.0.1 6379 2之后,否则会出现问题
  1. 分别进行启动redis主从以及哨兵6个服务。可以使用info replication 命令进行查看主,从服务启动状态,通过输出可以查看到主服务器有几个从服务器以及从服务器依赖那个主服务器及端口等等;
    例如查看主服务器状态、从服务器状态:
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6380>info replication
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:53007
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

5.上述配置完成了redis的主从复制,以及监控和故障迁移工作,需要注意的是master(主服务器)可以读写数据,但是从服务器只能进行读数据,不能进行写操作;

SpringBoot配置哨兵模式

1.redis.properties配置

#Matser的ip地址
redis.hostName=localhost
#端口号
redis.port=6379
#如果有密码
redis.password=
#客户端超时时间单位是毫秒 默认是2000
redis.timeout=10000  

#最大空闲数
redis.maxIdle=300  
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
#redis.maxActive=600
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
redis.maxTotal=1000  
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
redis.maxWaitMillis=1000  
#连接的最小空闲时间 默认1800000毫秒(30分钟)
redis.minEvictableIdleTimeMillis=300000  
#每次释放连接的最大数目,默认3
redis.numTestsPerEvictionRun=1024  
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
redis.timeBetweenEvictionRunsMillis=30000  
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true  
#在空闲时检查有效性, 默认false
redis.testWhileIdle=true 

#redis 哨兵监听的Redis Server的名称
spring.redis.sentinel.master=master
# comma-separated list of host:port pairs  哨兵的配置列表
spring.redis.sentinel.nodes=127.0.0.1:26379,127.0.0.1:36379,127.0.0.1:46379

连接工具类RedisConfig

package corn.util;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

/**
 * create by: 玉米
 * description:redis 连接工具类
 * create time: 16:42 2018/12/20
 *
  * @Param: null
 * @return
 */
@Configuration
@PropertySource("classpath:redis.properties")
public class RedisConfig {
    @Value("${redis.maxIdle}")
    private Integer maxIdle;

    @Value("${redis.maxTotal}")
    private Integer maxTotal;

    @Value("${redis.maxWaitMillis}")
    private Integer maxWaitMillis;

    @Value("${redis.minEvictableIdleTimeMillis}")
    private Integer minEvictableIdleTimeMillis;

    @Value("${redis.numTestsPerEvictionRun}")
    private Integer numTestsPerEvictionRun;

    @Value("${redis.timeBetweenEvictionRunsMillis}")
    private long timeBetweenEvictionRunsMillis;

    @Value("${redis.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${redis.testWhileIdle}")
    private boolean testWhileIdle;


    @Value("${redis.hostName}")
    private String hostName;

    @Value("${redis.port}")
    private Integer port;

    @Value("${redis.timeout}")
    private Integer timeout;

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

    @Value("${spring.redis.sentinel.nodes}")
    private String redisNodes;

    /**
     * JedisPoolConfig 连接池
     * @return
     */
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲数
        jedisPoolConfig.setMaxIdle(maxIdle);
        // 连接池的最大数据库连接数
        jedisPoolConfig.setMaxTotal(maxTotal);
        // 最大建立连接等待时间
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
        jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
        // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        // 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        // 在空闲时检查有效性, 默认false
        jedisPoolConfig.setTestWhileIdle(testWhileIdle);
        return jedisPoolConfig;
    }
    /**
     * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * redis哨兵配置
     * @return
     */
    @Bean
    public RedisSentinelConfiguration redisSentinelConfiguration(){
        RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
        String[] host = redisNodes.split(",");
        for(String redisHost : host){
            String[] item = redisHost.split(":");
            String ip = item[0];
            String port = item[1];
            configuration.addSentinel(new RedisNode(ip, Integer.parseInt(port)));
        }
        configuration.setMaster(master);
        return configuration;
    }
}

RedisComponent 工具类(声明:用到的User类就三个属性,可以自己进行指定,博文中未贴出实际代码)

package corn.util;

import corn.redis.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
 * create by: 玉米
 * description:redis 测试工具类
 * create time: 16:42 2018/12/20
 *
  * @Param: null
 * @return
 */
@Component
public class RedisComponent {

    @Autowired
    //操作字符串的template,StringRedisTemplate是RedisTemplate的一个子集
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    // RedisTemplate,可以进行所有的操作
    private RedisTemplate redisTemplate;

    public void set(String key, String value){
        ValueOperations ops = this.stringRedisTemplate.opsForValue();
        boolean bExistent = this.stringRedisTemplate.hasKey(key);
        if (bExistent) {
            System.out.println("this key is bExistent!");
        }else{
            ops.set(key, value);
        }
    }

    public String get(String key){
        return this.stringRedisTemplate.opsForValue().get(key);
    }

    public void del(String key){
        this.stringRedisTemplate.delete(key);
    }

    public void sentinelSet(User user){
        String key = null;
            key = user.getId();
        System.out.println(key);
        redisTemplate.opsForValue().set(key.toString(),user.toString());
    }

    public String sentinelGet(String key){
        return stringRedisTemplate.opsForValue().get(key);
    }
}

测试类SpringbootSentinelredisApplicationTests

package corn.redis.controller;

import corn.redis.model.User;
import corn.util.RedisComponent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * create by: 玉米
 * description:redis 测试类
 * create time: 16:42 2018/12/20
 *
  * @Param: null
 * @return
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootSentinelredisApplicationTests {

    @Autowired
    private RedisComponent redisComponet;

    @Test
    public void sentinelSet(){
        User user = new User();
        user.setId("0002");
        user.setAge(40);
        user.setName("狗蛋");

        redisComponet.sentinelSet(user);
    }

    @Test
    public void sentinelGet(){
        String str = redisComponet.get("0002");
        System.out.println(str);
    }

    @Test
    public void sentineldel(){
        redisComponet.del("0002");

    }

}

  1. 测试
    在Redis主从及哨兵运行无误的情况下,首先运行,向master(主服务)插入数据,然后进行将master主服务关闭,然后继续执行向Redis插入数据,如果正常插入不抛出异常,则基于SpringBoot的Redis的一主两从三哨兵模式搭建完毕;

总结

该博文主要记录了Redis 一主两从三哨兵的高可用方案,以及基于Spring Boot 进行搭建Redis 故障转移。测试可用
有什么描述不正确的地方烦请指正

你可能感兴趣的:(spring,boot学习)