深度好文_保姆级教程Redis高可用之哨兵站岗

Redis 哨兵高可用架构

前文介绍了 redis 主从模式的搭建,今天我们继续介绍哨兵模式。

sentinel 哨兵是特殊的 redis 服务,哨兵不提供读写服务,主要用来监控、提醒和自动故障转移;

监控:Sentinel 会不断的检查你的主服务器和从服务器是否运行正常。提醒:当被监控的某个 Redis 服务出现问题时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。自动故障转移:当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作,他会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端视图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。

哨兵架构图如下所示:

image-

哨兵架构下 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 yespidfile /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 命令# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0: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 redisroot      60899      1  0 23:36 ?        00:00:00 /usr/local/redis-6.2.6/src/redis-server *:6379root      60905      1  0 23:36 ?        00:00:00 /usr/local/redis-6.2.6/src/redis-server *:6380root      60913      1  0 23:36 ?        00:00:00 /usr/local/redis-6.2.6/src/redis-server *:6381root      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 26379127.0.0.1:26379> info# Sentinel 截取部分内容sentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0: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.confsed -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 redisroot      60899      1  0 23:36 ?        00:00:01 /usr/local/redis-6.2.6/src/redis-server *:6379root      60905      1  0 23:36 ?        00:00:01 /usr/local/redis-6.2.6/src/redis-server *:6380root      60913      1  0 23:36 ?        00:00:01 /usr/local/redis-6.2.6/src/redis-server *:6381root      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]# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0:name=mymaster,status=ok,address=172.16.38.12:6379,slaves=2,sentinels=3 # 注意这里是3个了

Sentinel 集群都启动完毕以后,会将哨兵的集群元数据信息追加到所有 Sentinel 的配置的最后面,例:

# Generated by CONFIG REWRITEprotected-mode nouser default on nopass ~* &* +@allsentinel myid 8b391bcbe23e598d55477c8bf6ac787cdbsentinel config-epoch mymaster 0sentinel leader-epoch mymaster 0sentinel current-epoch 0sentinel 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 daeb96500a3f87064feca0276eb6 # 感知到的其他哨兵节点sentinel known-sentinel mymaster 172.16.38.12 26379 3c8d4b1aedfba69fc9be # 感知到的其他哨兵节点

当哨兵感知到 master 信息变化后,同时还会修改之前配置的 myster 对应的主节点信息。

sentinel monitor mymaster 172.16.38.12 26380 2

哨兵集群搭建完毕后,可通过客户端获取 redis master 节点信息。

哨兵集群下从节点一般只做备份使用。

我们从 jedis 客户端初始化中科院发现 redis.clients.jedis.JedisSentinelPool#initSentinels 用于在客户端启动的时候从哨兵节点获取 redis 集群信息。关键命令如下:

./src/redis-cli -p 26379127.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"

哨兵Leader 选举流程

当一个 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。

不过为了高可用,一般都推荐至少部署三个哨兵节点。

哨兵注意事项 Q&A

如果哨兵全挂了,集群是否可用?

哨兵全部挂掉后,只要不重启应用,还是可以继续与 redis 实例进行通信的,但是重启后就不可用了。

每个哨兵配置的是 master 主节点信息,哨兵会从 master 获取从节点信息。

哨兵与redis集群内节点是一一对应吗?

哨兵节点与 redis 实例可以不一一对应,但是建议使用奇数个哨兵节点监控 redis 集群。

代码示例

JedisJedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(20);poolConfig.setMaxIdle(10);poolConfig.setMinIdle(5);// 同配置文件内的名称 sentinel monitor mymaster 172.16.38.8 6379 2String 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 节点。我们从程序打印可以看到当哨兵集群下出下主从切换的时候,会有几秒到几十秒的不可用时间,时间还是很长的,后续文章我们会介绍集群架构,当发生切换时是很快的。

spring boot 整合redis

使用新建 maven 项目,添加 redis starter 依赖,注意不要选错,或者直接引入以下 maven 配置。

image-<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)@EnableCachingpublic 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;    }}

关注,后台回复 redis主从 获取完整的配置文件。

关注,后台回复 redis主从 获取完整的配置文件。