前文介绍了 redis 主从模式的搭建,今天我们继续介绍哨兵模式。
sentinel 哨兵是特殊的 redis 服务,哨兵不提供读写服务,主要用来监控、提醒和自动故障转移;
哨兵架构图如下所示:
image-20220325141559769
哨兵架构下 client 端第一次从 哨兵节点获取 redis 的主节点,后续就直接访问 redis 的主节点,不会每次都通过 sentinel 代理访问 redis 主节点。
当 redis 主节点发生变化,哨兵会第一时间感知到,并且将新的 redis 主节点通知给 client 端,redis client 端一般都实现了订阅功能,订阅 sentinel 发布的节点变动消息。
搭建哨兵之前,先按照主从搭建步骤,搭建好主从复制。然后按照下面的步骤操作:
# 将redis根目录下sentinel.conf配置文件复制到26379目录下
cp sentinel.conf sentinel/26379/
# 修改配置文件内容
vim sentinel/26379/sentinel.conf
# 后台启动
daemonize yes
pidfile /var/run/redis-sentinel-26379.pid
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
# quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 172.16.38.12 6379 2
# 38.6 和38.7 两个节点也按照以上内容修改,注意修改IP为自主节点的ip地址
# 启动sentinel哨兵
./src/redis-sentinel ./sentinel.conf
# 连接哨兵查看集群信息
./src/redis-cli -p 26379
# 连接以后执行 info 命令
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.16.38.6:6379,slaves=2,sentinels=3
文件修改完毕后,使用 redis-sentinel
启动 sentinel 哨兵实例。
# 注意下方执行的命令是
/usr/local/redis-6.2.6/src/redis-sentinel sentinel/26379/sentinel.conf
# 验证是否启动成功
[root@beifeng redis-6.2.6]# ps -ef|grep redis
root 60899 1 0 23:36 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-server *:6379
root 60905 1 0 23:36 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-server *:6380
root 60913 1 0 23:36 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-server *:6381
root 61125 1 0 23:48 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-sentinel *:26379 [sentinel]
# 连接哨兵节点,查看信息
[root@beifeng redis-6.2.6]# ./src/redis-cli -p 26379
127.0.0.1:26379> info
# Sentinel 截取部分内容
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.16.38.12:6379,slaves=2,sentinels=1
执行 info
命令检查哨兵实例启动成功,接下来修改另外两个节点配置,并启动。
# 复制文件
cp sentinel/26379/sentinel.conf sentinel/26380/
cp sentinel/26379/sentinel.conf sentinel/26381/
# 替换关键信息
sed -i 's/26379/26380/g' sentinel/26380/sentinel.conf
sed -i 's/26379/26381/g' sentinel/26381/sentinel.conf
# 启动节点
/usr/local/redis-6.2.6/src/redis-sentinel sentinel/26380/sentinel.conf
/usr/local/redis-6.2.6/src/redis-sentinel sentinel/26381/sentinel.conf
# 验证
[root@beifeng redis-6.2.6]# ps -ef|grep redis
root 60899 1 0 23:36 ? 00:00:01 /usr/local/redis-6.2.6/src/redis-server *:6379
root 60905 1 0 23:36 ? 00:00:01 /usr/local/redis-6.2.6/src/redis-server *:6380
root 60913 1 0 23:36 ? 00:00:01 /usr/local/redis-6.2.6/src/redis-server *:6381
root 61125 1 0 23:48 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-sentinel *:26379 [sentinel]
root 61211 1 0 23:52 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-sentinel *:26380 [sentinel]
root 61217 1 0 23:52 ? 00:00:00 /usr/local/redis-6.2.6/src/redis-sentinel *:26381 [sentinel]
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.16.38.12:6379,slaves=2,sentinels=3 # 注意这里是3个了
Sentinel 集群都启动完毕以后,会将哨兵的集群元数据信息追加到所有 Sentinel 的配置的最后面,例:
# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* &* +@all
sentinel myid 8b391b280507cbe23e598d55477c8bf6ac787cdb
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel current-epoch 0
sentinel known-replica mymaster 172.16.38.12 6381 # 代表redis主节点的从节点信息
sentinel known-replica mymaster 172.16.38.12 6380 # 代表redis主节点的从节点信息
sentinel known-sentinel mymaster 172.16.38.12 26381 d514797ae793226b96500a3f87064feca0276eb6 # 感知到的其他哨兵节点
sentinel known-sentinel mymaster 172.16.38.12 26379 3c8d4b1aed928130059fba9529904610469fc9be # 感知到的其他哨兵节点
当哨兵感知到 master 信息变化后,同时还会修改之前配置的 myster 对应的主节点信息。
sentinel monitor mymaster 172.16.38.12 26380 2
哨兵集群搭建完毕后,可通过客户端获取 redis master 节点信息。
哨兵集群下从节点一般只做备份使用。
我们从 jedis 客户端初始化中科院发现 redis.clients.jedis.JedisSentinelPool#initSentinels
用于在客户端启动的时候从哨兵节点获取 redis 集群信息。关键命令如下:
./src/redis-cli -p 26379
127.0.0.1:26379> SENTINEL GET-MASTER-ADDR-BY-NAME mymaster # 获取 master 节点信息
1) "172.16.38.12"
2) "6379"
127.0.0.1:26379> SENTINEL slaves mymaster # 获取所有从节点信息
1) 1) "name"
2) "172.16.38.12:26380"
3) "ip"
4) "172.16.38.12"
5) "port"
6) "26380"
当一个 master 服务器被某 sentinel 视为下线状态后,该 sentinel 会与其他 sentinel 协商选出 sentinel 的 leader 进行故障转移工作。每个发现 master 服务器进入下线的 sentinel 都可以要求其他 sentinel 选自己为 sentinel 的 leader,选举是先到先得。同时每个 sentinel 每次选举都会自增配置纪元(选举周期),每个纪元中只会选择一个 sentinel 的 leader。
如果所有超过一半的 sentinel 选举某 sentinel 作为 leader。之后该 sentinel 进行故障转移操作,从存活的 slave 中选举出新的 master,这个选举过程跟集群的 master 选举很类似。
哨兵集群只有一个哨兵节点,redis 的主从也能正常运行以及选举 master,如果 master 挂了,那唯一的那个哨兵节点就是哨兵 leader 了,可以正常选举新 master。
不过为了高可用,一般都推荐至少部署三个哨兵节点。
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(10);
poolConfig.setMinIdle(5);
// 同配置文件内的名称 sentinel monitor mymaster 172.16.38.8 6379 2
String masterName = "mymaster";
Set<String> sentinels = new HashSet<>();
// 哨兵的ip和端口号
sentinels.add(new HostAndPort("172.16.38.6", 26379).toString());
sentinels.add(new HostAndPort("172.16.38.7", 26379).toString());
sentinels.add(new HostAndPort("172.16.38.8", 26379).toString());
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, poolConfig);
int i = 0;
while (true) {
try (Jedis jedis = jedisSentinelPool.getResource();) {
i++;
jedis.set("key-sent-" + i, "" + i);
Thread.sleep(1000);
System.out.println(jedis.get("key-sent-" + i));
} catch (Exception e) {
e.printStackTrace();
}
}
代码里写了个死循环,目的是验证当 redis 主节点挂掉以后 slave 节点是否会切换成 master 节点。我们从程序打印可以看到当哨兵集群下出下主从切换的时候,会有几秒到几十秒的不可用时间,时间还是很长的,后续文章我们会介绍集群架构,当发生切换时是很快的。
使用 https://start.spring.io/ 新建 maven 项目,添加 redis starter 依赖,注意不要选错,或者直接引入以下 maven 配置。
image-20220325164349620
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置 application.yml 文件,springBoot 高版本以后使用 lettuce 连接 redis
spring:
redis:
timeout: 3000
# host: 172.16.38.6
lettuce:
pool:
max-idle: 50
min-idle: 10
max-active: 100
max-wait: 1000
database: 0
sentinel: # 哨兵配置
master: mymaster # 配置文件配置的集群名称
nodes: 172.16.38.12:26379,172.16.38.12:26380,172.16.38.12:26381 # 哨兵的ip和端口
springBoot 使用 RedisTemplate 操作 redis 导致 redis key 乱码问题解决
我们在使用 springboot 的时候,如果不做任何配置直接使用 RedisTemplate 操作 redis key 会出现 \xac\xed\x00 样式的乱码,可按如下配置进行修改,彻底解决 redis key 乱码的问题。。
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate redisCacheTemplate(RedisTemplate redisTemplate) {
RedisSerializer keySerializer = new StringRedisSerializer();
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashKeySerializer(keySerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
return redisTemplate;
}
}