1. 基本介绍
Sentinel 哨兵模式
- Sentinel 哨兵模式是为了弥补主从复制集群中主机宕机后,主备切换的复杂性而演变出来的。哨兵顾名思义,就是用来监控的,主要作用就是监控主从集群,自动切换主备,完成集群故障转移。
Sentinel 哨兵Sentinel 哨兵介绍
Sentinel 哨兵本质上是一个运行在特殊模式下的Redis实例,只是初始化的过程和工作与普通的 Redis不同,本质上也是一个单独的进程。
Sentinel 哨兵 是Redis的高可用解决方案:一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在主服务器下线时可以自动切换从服务器升级为主服务器。
1、Sentinel系统
下图是一个简单的Sentinel系统架构图,一个Sentinel系统监视一个主从集群,其中server1是Redis主服务器,server2/3/4是Redis 从服务器。主从之间利用上面的主从复制来达到主从一致。而Sentinel系统监视整个主从集群。
2、Sentinel故障转移
当Sentinel系统察觉到Server1主服务器下线时,就会终止server2/3/4的复制。
同时Sentinel将server2升级为主服务器,server3/4从新的主服务器进行复制。同时等待server1的再次上线。
Sentinel系统也可以主动降级主服务为从服务器,将从服务器升级为主服务器。
Sentinel 哨兵监控过程
Sentinel 哨兵监控集群过程:
- 命令 Sentinel哨兵通过发送命令,让redis服务器返回运行状态。发布订阅 当主服务器状态发生变化时,Sentinel哨兵通过
- 发布订阅模式通知其他从服务器。
Sentinel 哨兵故障转移
Sentinel 故障转移:
- 1、Sentinel系统中的Sentinel实例每隔1s就像集群发送PING命令
- 2、如果集群中有实例的回复Sentinel实例时间超过了 down-after-milliseconds,那么这个实例就会发送PING命令的Sentinel实例被主观下线
- 3、那么什么时候会客观下线呢?需要Sentinel系统中其他实例也确认集群中该实例主管下线。
- 如果master主服务器被标记为主观下线,则Sentinel系统中监视master的Sentinel进程需要以每秒一次的频率确认Master是否进入主管下线状态
- 4、当有足够的Sentinel实例(取决于配置)确认Master进入了主管下线,则Master会被标记为客观下线。
Sentinel 哨兵优缺点
优点:
1、哨兵模式基于主从复制,因此主从复制的优点哨兵都具备
2、哨兵具备了主从切换和故障转移,因此集群有了更高的可用性
缺点:
1、Redis较难支持在线扩容,在线扩容比较复杂。
总结:
sentinel 哨兵主要用来监控redis主从集群,提高了redis 主从集群的可用性。
2. 引导
建议先参考主从模式搭建教程:
Sentinel模式是基于主从模式的嘛,因此要实现Sentinel集群模式的简单搭建需要提前做好主从集群模式的搭建。
想尝试主从怎么搭建的参考上一篇博客:
http://t.csdnimg.cn/emWe0
注意点:注意点:注意点:
**【必须】
**参考主从只需参考centos下的redis.conf配置文件那里即可,让三个redis服务跑起来就ok了,其他的无需参考,主从归主从,哨兵归哨兵嘛。
通俗点:从标题1看到标题5即可
3. 基本使用(基于Linux的centos系统) (window下的操作也差不多)
3.1在你的windows下准备三个sentinel的配置文件
sentinel01.conf sentinel02.conf sentinel03.conf
sentinel01.conf
bind 0.0.0.0
port 1001
sentinel announce-ip "192.168.6.128"
# 后面的2 表示有2个哨兵认为主库挂了就是客观下线
# 就会开始选举
# 5000 表示每5秒钟检测一次主库是否挂掉
sentinel monitor mymaster 192.168.6.128 6380 2
sentinel failover-timeout mymaster 5000
# linux中 解除redis保护 允许外部连接
protected-mode no
# 后台访问
daemonize yes
sentinel02.conf
bind 0.0.0.0
port 1002
sentinel announce-ip "192.168.6.128"
# 后面的2 表示有2个哨兵认为主库挂了就是客观下线
# 就会开始选举
# 5000 表示每5秒钟检测一次主库是否挂掉
sentinel monitor mymaster 192.168.6.128 6380 2
sentinel failover-timeout mymaster 5000
# linux中 解除redis保护 允许外部连接
protected-mode no
# 后台访问
daemonize yes
sentinel03.conf
bind 0.0.0.0
port 1003
sentinel announce-ip "192.168.6.128"
# 后面的2 表示有2个哨兵认为主库挂了就是客观下线
# 就会开始选举
# 5000 表示每5秒钟检测一次主库是否挂掉
sentinel monitor mymaster 192.168.6.128 6380 2
sentinel failover-timeout mymaster 5000
# linux中 解除redis保护 允许外部连接
protected-mode no
# 后台访问
daemonize yes
3.2 利用文件传输工具将其传出到服务器
3.3. 上传完之后启动这三个哨兵配置
前提
:redis主从配置的三个服务必须开启
启动命令如下:
redis-sentinel sentinel01.conf
redis-sentinel sentinel02.conf
redis-sentinel sentinel03.conf
# 查看redis进程
ps -ef|grep redis|grep -v grep
3.4 redis连接工具进行连接 【工具安装就略过了蛤】
填写主机IP等信息
其他两个同理
最后连接效果
这时切换到主库往下滑找到role这一行,
此效果证明主从配置搭建没问题
4. redis哨兵模式整合springboot简单的单元测试
gitee代码测试地址:https://gitee.com/crqyue/springboot-redis-sentinel.git
4.1 pom依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.10.0</version>
</dependency>
4.2 yaml配置redis连接
spring:
datasource: # oracle数据库连接 - 可以切换为自己的数据库
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@localhost:1521:orcl
username: oa
password: root
type: com.alibaba.druid.pool.DruidDataSource
redis: # redis哨兵配置
sentinel:
master: mymaster
nodes: 192.168.6.128:1001,192.168.6.128:1002,192.168.6.128:1003
mybatis-plus:
type-aliases-package: com.cy.entity
4.3 config包下的RedisConfig类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory); // 启动时注入连接对象
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 设置序列化器
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate; // 返回模板对象
}
}
4.4 util包下的RedisUtil类
@Component
public class RedisUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key , long time) {
try {
if (time > 0) {
redisTemplate.expire(key , time , TimeUnit.SECONDS);
}
return true;
} catch ( Exception e ) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key , TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch ( Exception e ) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(Arrays.asList(key));
}
}
}
// ============================= key相关操作 =============================
/**
* 可以实现分布式锁
*
* @param key
* @param value
* @return key存在则返回false,不存在则存储key-value并返回true
*/
public boolean setnx(String key , String value) {
return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key , value));
}
/**
* 可以实现分布式锁
*
* @param key
* @param value
* @param timeout
* @param unit
* @return key存在则返回false,不存在则存储key-value并返回true
*/
public boolean setnx(String key , String value , long timeout