社区编辑申请
注册/登录
Redis Cluster集群,当Master宕机,主从切换,客户端报错 Timed Out
开发 前端
一个高并发系统肯定少不了缓存的身影,为了保证缓存服务的高可用,我们通常采用 Redis Cluster 集群模式。

大家好,我是Tom哥。

性能不够,缓存来凑。

一个高并发系统肯定少不了缓存的身影,为了保证缓存服务的高可用,我们通常采用 Redis Cluster 集群模式。

描述:

集群部署采用了 3主3从 拓扑结构,数据读写访问master节点, slave节点负责备份。

随便登录一台 redis 节点,都可以看到集群的slot的槽位分步区间,以及对应的主从节点映射关系。

127.0.0.1:8001> cluster slots
1) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 8003
3) "6c574c9d1323c69ebc73a5977bcbd3d4c073a4d4"
4) 1) "127.0.0.1"
2) (integer) 8006
3) "123d0b157078925743ac1deb96be8c3395d7d038"
2) 1) (integer) 0
2) (integer) 5460
3) 1) "127.0.0.1"
2) (integer) 8001
3) "99bc05e81ef0035a4ab2d13cbae2599425b7ed7d"
4) 1) "127.0.0.1"
2) (integer) 8004
3) "402e900ef364ce9382beddf92747cf28e3ea9c2f"
3) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 8002
3) "fda6a9e49205a52418c0bca4c66c981066017a3c"
4) 1) "127.0.0.1"
2) (integer) 8005
3) "24a1e23f6cbfb761234970b66043d562e79e3d9c"

人为模拟,master-1 机器意外宕机。

docker stop c1dff012392d

此时,Redis Cluster 集群能自动感知,并自动完成主备切换,对应的slave会被选举为新的master节点。

看下 redis cluster 集群最新的主从关系。

看似也没什么问题,一切正常。

此时 Spring Boot 应用依然在线服务,当我们再尝试操作缓存时,会报错。

问题边界还是非常清晰的。

Redis Cluster 集群已经完成了切换。

但是 Spring Boot 客户端没有动态感知到 Redis Cluster 的最新集群信息

原因分析:

SpringBoot 2.X 版本, Redis默认的连接池采用 Lettuce。

当Redis 集群节点发生变化后,Letture默认是不会刷新节点拓扑。

解决方案:

将 Letture 二方包仲裁掉。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.12.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>

然后,引入 Jedis 相关二方包。

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>

编译代码,并重新启动 SpringBoot 微服务,万事俱备,只欠再次验证。

重新模拟将 127.0.0.1:8001 master 节点宕机,看看系统的日志。

[2022-03-17 18:03:34:595] - master /127.0.0.1:8001 used as slave
[2022-03-17 18:03:34:596] - slave redis://127.0.0.1:8004 removed for slot ranges: [[0-5460]]
[2022-03-17 18:03:34:611] - 1 connections initialized for /127.0.0.1:8004
[2022-03-17 18:03:34:639] - /127.0.0.1:8001 master and related slaves: [addr=redis://127.0.0.1:8004] removed
[2022-03-17 18:03:34:641] - 24 connections initialized for /127.0.0.1:8004
[2022-03-17 18:03:34:655] - 1 connections initialized for /127.0.0.1:8004
[2022-03-17 18:03:34:678] - master: redis://127.0.0.1:8004 added for slot ranges: [[0-5460]]
[2022-03-17 18:03:34:678] - 24 connections initialized for /127.0.0.1:8004

从打印的日志来看,客户端已经感知到了主备切换,并与最新的主节点 127.0.0.1:8004 初始化了 24 个连接。

然后,回归业务功能,读写缓存 数据也都是操作最新的主节点。

还有一种方案:刷新节点拓扑视图。

Lettuce 官方描述:

https://github.com/lettuce-io/lettuce-core/wiki/Redis-Cluster#user-content-refreshing-the-cluster-topology-view。

Lettuce 处理 Moved 和 Ask 永久重定向,由于命令重定向,必须刷新节点拓扑视图。而自适应拓扑刷新(Adaptive updates)与定时拓扑刷新(Periodic updates)默认关闭。

解决方案:

  • 调用 RedisClusterClient.reloadPartitions。
  • 后台基于时间间隔的周期刷新。
  • 后台基于持续的断开 和 移动、重定向 的自适应更新。

编写代码

@Bean(destroyMethod = "destroy")
public LettuceConnectionFactory lettuceConnectionFactory() {
//
ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
// ,Redis
.enableAllAdaptiveRefreshTriggers()
// (认30)
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
//
.enablePeriodicRefresh(Duration.ofSeconds(20))
.build();
ClientOptions clientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions)
.build();
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.poolConfig(genericObjectPoolConfig(redisProperties.getJedis().getPool()))
.clientOptions(clientOptions)
.commandTimeout(redisProperties.getTimeout()) //认RedisURI.DEFAULT_TIMEOUT 60
.build();
List<String> clusterNodes = redisProperties.getCluster().getNodes();
Set<RedisNode> nodes = new HashSet<RedisNode>();
clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.valueOf(address.split(":")[1]))));
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
clusterConfiguration.setClusterNodes(nodes);
clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(clusterConfiguration, clientConfig);
// 线认true,false
// lettuceConnectionFactory.setShareNativeConnection(false);
// , 访
// lettuceConnectionFactory.resetConnection();
return lettuceConnectionFactory;
}
责任编辑:姜华 来源: 微观技术
相关推荐

2022-06-12 06:48:34

2022-05-23 07:48:10

zabbix监控CentOS7

2022-05-31 08:04:03

Redis高可用集群

2022-06-15 08:21:49

Linux运维工程师

2022-05-31 09:01:44

RedisRDBAOF

2022-06-23 11:42:22

MySQL数据库

2022-06-28 08:40:16

LokiPromtail日志报警

2022-06-28 12:35:21

DockerPython

2022-06-28 14:47:43

数据中心服务器科技

2022-06-14 14:18:46

架构秒杀高并发

2022-05-05 09:27:31

Linux服务器优化

2022-06-06 14:35:59

KubevirtKubernetes虚拟机

2022-06-18 09:26:00

Flink SQLJoin 操作

2022-06-09 13:45:18

vivoK8S集群Kubernetes

2022-05-09 15:08:56

存储厂商NFV领域华为

2022-06-12 10:55:48

AmbariApachePMC 成员

2022-06-04 07:26:47

Thanos集群Prometheus

2022-06-02 07:13:12

Python3.11编程语言

2022-06-23 06:43:55

异常数据上报异常监控sdk阿波罗移动端

2022-06-19 14:13:29

tmateLinux

同话题下的热门内容

哪个版本的JVM最快?无代码软件发展简史及未来趋势携程基于 GraphQL 的前端 BFF 服务开发实践为什么会存在 1px 问题?怎么解决?一文搞定常考Vue-Router知识点EcmaScript 2022 正式发布,有哪些新特性?一文详解|增长那些事儿远程医疗:优势、前景和现有IT解决方案

编辑推荐

太厉害了,终于有人能把TCP/IP协议讲的明明白白了!牛人5次面试腾讯不成功的经验HBase原理–所有Region切分的细节都在这里了Javascript如何监听页面刷新和关闭事件如何搭建一个HTTPS服务端
我收藏的内容
点赞
收藏

51CTO技术栈公众号