前言
之前项目需求部署redis高可用,走了很多弯路以及相关配置来回折腾浪费了很多时间,特地记录下。
主从模式:实现多台redis实例进行服务运行,并且数据相互同步;
哨兵模式:实现主服务器和从服务器进行监听,当主服务器宕机,会立马进行主服务选举, 选出新的主服务器。
一、redis安装
链接:https://pan.baidu.com/s/1us7TeMD2xCF2Pxm-mu-nGw
提取码:ae4u
1.下载redis
网盘提供的是windows下的5.0.10版本,若是想要其他版本的需要自行百度;
2.安装
直接双击进行安装,全部就是next即可,全程都不需要更改的,就是注意安装位置,不要放在C盘就行。
安装好就是这样的,我是放在E盘下:E:\\development\redisPool\redis下的
3.再把redis文件复制一份
把redis先复制一份,并且修改两个文件,master_6379做主服务,salve_6380做从服务
二、主从服务配置
1.修改配置文件
修改两个文件中的redis.windows.conf文件如下配置,其他不改,要删除如下配置前面的#符号,不然被注释不会采用新的配置信息。
master_6379 文件中redis.windows.conf修改配置如下:
#是否开启保护模式,选择no
protected-mode no
# 配置redis从服务器密码
requirepass 123456
# 配置redis主服务器密码,requirepass和masterauth两个密码必须一样
masterauth 123456#是否是集群模式的标志,这里改为no
cluster-enabled no# 改为0.0.0.0 便于其他服务器可以访问到此redis服务
bind 0.0.0.0
salve_6380 文件中redis.windows.conf修改配置如下:
# salve_6380 比master_6379多个端口的修改,master_6379端口默认6379
port 6380
protected-mode no# 主节点ip和端口,即master_6379服务的ip和端口
slaveof 127.0.0.1 6379
requirepass 123456
masterauth 123456
cluster-enabled no
bind 0.0.0.0
2.启动两个主从服务
windows下进行cmd命令,打开终端,进入到redis根目录,输入如下命令:
redis-server.exe ./redis.windows.conf
启动主服务,一定要切换到主redis服务的根目录master_6379下执行命令否则会报错:
启动从服务,一定要切换到主redis服务的根目录salve_6380下执行命令否则会报错:
如此,两个主从服务算是一直运行中,不能关闭命令窗口,关闭服务便停止了。
3.验证服务是否正常运行
随便进入某个redis服务的根目录,通过终端连接redis,输入命令:
redis-cli -p 6379 # -p 指定连接redis的端口号
auth 123456 # 输入redis密码,在redis.windows.conf文件中设置的密码
get key # 查询某个key的值
info replication # 查询当前服务是主服务还是,从服务
或者
从命令可以看出6380的服务role为slave,即从服务,而6379的服务是master,即主服务;
且6379服务下redis的connected_slaves为1,slave0:ip=20.20.0.219,port=6380,state=online
也指出从服务的端口,以及状态都是正常的。
4.测试从主服务写入数据从服务是否可以正常同步过来
在主服务6379下输入命令,设置数据:
set test redis
在从服务6380下输入命令,获取数据:
set test
如上可以看出两台redis正常工作,且一主一从模式,并且数据也能够达到同步效果。直接关闭窗口即可。
现在配置的方式是只能在主服务器下进行写数据操作
但是在某个条件下主服务宕机了,就无法实现业务正常运作了,因为从服务无法进行写数据,所以这时候我们要在主从模式基础上进行哨兵配置,便于监听主服务宕机,可以立马将从服务升级为主服务,保证业务可用。哨兵模式如下:
三、哨兵模式配置
1.增加sentinel.conf文件
分别在master_6379、salve_6380文件夹下新建文件sentinel.conf,文件内容如下:
【master_6379】主服务的sentinel.conf内容
# 当前Sentinel服务运行的端口
port 26379
# 禁止保护模式
protected-mode no
# 哨兵监听的主服务器 后面的1表示主机挂掉以后进行投票,只需要1票就可以从机变主机
sentinel monitor mymaster 127.0.0.1 6379 1
# 3s内mymaster无响应,则认为mymaster宕机了
sentinel down-after-milliseconds mymaster 3000
#如果10秒后,mysater仍没启动过来,则启动failover
sentinel failover-timeout mymaster 10000
# 设置哨兵sentinel连接主从的密码,主从必须设置一样密码,没有的话不用设置
sentinel auth-pass mymaster 123456
# 执行故障转移时, 最多有1个从服务器同时对新的主服务器进行同步
sentinel config-epoch mymaster 21
【salve_6380】从服务的sentinel.conf内容
# 当前Sentinel服务运行的端口
port 26380
# 禁止保护模式
protected-mode no
# 哨兵监听的主服务器 后面的1表示主机挂掉以后进行投票,只需要1票就可以从机变主机
sentinel monitor mymaster 127.0.0.1 6379 1
# 3s内mymaster无响应,则认为mymaster宕机了
sentinel down-after-milliseconds mymaster 3000
#如果10秒后,mysater仍没启动过来,则启动failover
sentinel failover-timeout mymaster 10000
# 设置哨兵sentinel连接主从的密码,主从必须设置一样密码,没有的话不用设置
sentinel auth-pass mymaster 123456
# 执行故障转移时, 最多有1个从服务器同时对新的主服务器进行同步
sentinel config-epoch mymaster 21
2.增加哨兵sentinel启动脚本
分别在master_6379、salve_6380文件夹下新建文件startSentinel.bat,文件内容如下:
redis-server.exe sentinel.conf --sentinel
3.增加redis启动脚本
分别在master_6379、salve_6380文件夹下新建文件startup.bat,不然每次都得在终端下输入命令,文件内容如下:
redis-server.exe ./redis.windows.conf
创建好的文件如下:
4.验证哨兵模式
先分别双击执行主从服务redis、再执行主从两个哨兵,顺序如下:
1.master_6379下:startup.bat
2.slave_6380下:startup.bat
3.master_6379下:startSentinel.bat
4.slave_6380下:startSentinel.bat
按照2.3步骤方式进行验证redis主从服务是否正常,如下:都是正常的;
关闭主服务6379,shutdown,两个哨兵均不能关闭,再验证6380是否可以成为主服务
可见6380成为主服务了
再把6379,启动,验证是不是应该成为从服务
从信息上面看,把6379启动之后,6380依然是主服务,只是新增一个从节点的连接,也就是说6379被重启之后就会成为从节点。
至此主从模式+哨兵模式部署完成。
四、Springboot 访问主从哨兵模式的redis
1.增加一个配置文件RedisClusterConfig
import com.chinamobile.zj.util.Constant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* redis主从模式,避免单节点redis宕机引发服务不可用问题
*/
@Configuration
@EnableAutoConfiguration
@Slf4j
public class RedisClusterConfig{
@Bean
public JedisSentinelPool testSentinel() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大空闲连接数, 默认8个
jedisPoolConfig.setMaxIdle(10);
// 最大连接数, 默认8个
jedisPoolConfig.setMaxTotal(16);
//最小空闲连接数, 默认0
jedisPoolConfig.setMinIdle(8);
// 获取连接时的最大等待毫秒数,10s(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
jedisPoolConfig.setMaxWaitMillis(10000);
//对拿到的connection进行validateObject校验;如果为true,则得到的jedis实例均是可用的;
jedisPoolConfig.setTestOnBorrow(true);
jedisPoolConfig.setTestOnReturn(true);
//Idle时进行连接扫描
jedisPoolConfig.setTestWhileIdle(true);
//表示idle object evitor两次扫描之间要sleep的毫秒数
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(30000);
//表示idle object evitor每次扫描的最多的对象数
jedisPoolConfig.setNumTestsPerEvictionRun(10);
//表示一个对象至少停留在idle状态的最短时间,才能被idle object evitor扫描并驱逐;只有在timeBetweenEvictionRunsMillis大于0时才有意义
jedisPoolConfig.setMinEvictableIdleTimeMillis(60000);
// 创建连接池
Set<String> sentinels = new HashSet<>(Arrays.asList("127.0.0.1:26379","127.0.0.1:26380"));
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels,jedisPoolConfig,"123456");
return pool;
}
}
2.redis读写数据
// 作为静态工具方法方式的访问
@Component
@Slf4j
public class RedisUtils{
private static JedisSentinelPool jedisSentinelPool;
@Autowired
public void setRedisTemplate(JedisSentinelPool jedisSentinelPool) {
RedisUtils.jedisSentinelPool = jedisSentinelPool;
}
public static String hSet(String key,String value) {
String result = jedisSentinelPool.getResource().set(key,value);
return result;
}
public static Object hGet(String key) {
String o = jedisSentinelPool.getResource().get(key);
return o;
}
}
// service 层的访问
@Service
@Transactional
@Slf4j
public class SSOLoginSv {
@Autowired
private JedisSentinelPool jedisSentinelPool;
public String getValue(){
return jedisSentinelPool.getResource().get("test");
}
public String setValue(){
return jedisSentinelPool.getResource().set("test","demo");
}
}
3.问题
经过一段时间运行发现,项目会时不时报错:报错内容为资源池不够了,主要原因是:
1.资源没有及时释放;
2.redis没有配置空闲时间超过某个数值,连接自动关闭。
所以调整的方式如下:
1.在redis中get/set之后进行连接池释放操作:
/**
* 释放jedis资源
*
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null) {
jedisSentinelPool.returnResource(jedis);
}
}
/**
* Jedis对象出异常的时候,回收Jedis对象资源
*
* @param jedis
*/
public static void returnBrokenResource(final Jedis jedis) {
if (jedis != null) {
jedisSentinelPool.returnBrokenResource(jedis);
}
}
public static String setVlaue(String key,String value) {
Jedis resource = null;
String result = "";
try{
resource = jedisSentinelPool.getResource();
result = resource.set(key,value);
}catch (Exception e) {
returnBrokenResource(resource);
e.printStackTrace();
} finally {
returnResource(resource);
}
return result;
}
public static Object getValue(String key) {
Jedis resource = null;
String o = "";
try{
resource = jedisSentinelPool.getResource();
o = resource.get(key);
}catch (Exception e) {
returnBrokenResource(resource);
e.printStackTrace();
} finally {
returnResource(resource);
}
return o;
}
2.在redis配置中设置超时关闭连接机制:
执行如下命令:
# 连接redis主机
redis-cli -h reis主机ip -p redis服务端口
# 查看redis主机设置多长时间空闲便关闭连接,若是返回0,便是此功能关闭,永不释放
config get timeout
# 修改超时关闭连接时长,秒为单位
config set timeout 600
3.完毕
欢迎各位大神有什么错误地方指出