Redis 哨兵模式作为Redis 的一种高可用方案,实现了主从复制、监控,故障转移等工作,在一定程度上保证了Redis的高可用,避免了因Redis服务宕机导致缓存服务不可用情况;本博文主要是基于Spring Boot进行搭建的Redis Sentinel(哨兵模式),采用的是 一主两从三哨兵
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之后,否则会出现问题
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(主服务器)可以读写数据,但是从服务器只能进行读数据,不能进行写操作;
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<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> 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<Object,Object> redisTemplate;
public void set(String key, String value){
ValueOperations<String, String> 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");
}
}
该博文主要记录了Redis 一主两从三哨兵的高可用方案,以及基于Spring Boot 进行搭建Redis 故障转移。测试可用
有什么描述不正确的地方烦请指正