当前位置: 首页 > 科技观察

RedisCluster集群,当master宕机,主从切换,客户端报错TimedOut

时间:2023-03-11 20:19:49 科技观察

大家好,我是Tom。如果性能不够,缓存会补上。高并发系统必须有缓存。为了保证缓存服务的高可用,我们通常使用RedisCluster集群模式。说明:集群部署采用3主3从拓扑结构,数据读写访问主节点,从节点负责备份。只要登录一个redis节点,就可以看到集群的slots的步长间隔和对应的主从节点映射关系。127.0.0.1:8001>集群插槽1)1)(整数)109232)(整数)163833)1)“127.0.0.1”2)(整数)80033)“6c574c9d1323c69ebc73a5977bcbd3d4c074a7)4d4”0.2)(整数)80063)“123D0B157078925743AC1DEB96BE8C3395D7D7D038”2)1)1)(Integer)02)(Integer)54603)54603)1)1)“127.0.0.0.1”2)(127.0.0.1“2)(整数)80043)”402E900EF364CE9382BEDDF92747CF28E3EA9C2F“3)1)1)(Integer)54612)(Integer)2)(Integer)(Integer)109223)109223)1)1)1)“127.0.0.11”2))1)"127.0.0.1"2)(integer)80053)"24a1e23f6cbfb761234970b66043d562e79e3d9c"人工模拟,master-1机器意外崩溃。dockerstopc1dff012392d此时RedisCluster集群可以自动感知并自动完成主备切换,相应的slave会被选举为新的master节点。查看rediscluster集群最新的主从关系。好像没什么问题,一切正常。此时SpringBoot应用还在在线服务。当我们再次尝试操作缓存时,就会报错。问题边界还是很清晰的。RedisCluster集群已经完成切换。但是SpringBoot客户端并没有动态感知RedisCluster的最新集群信息原因分析:在SpringBoot2.X版本中,Redis默认的连接池使用的是Lettuce。当Redis集群节点发生变化时,Letture默认不会刷新节点拓扑。解决方案:仲裁Letture二方包。org.springframework.bootspring-boot-starter-data-redis2.3.12.RELEASEio.lettucelettuce-core然后引入Jedis相关的二方包。redis.clientsjedis3.7.0编译代码,重启SpringBoot微服务,万事俱备,只欠再次验证。重新模拟127.0.0.1:8001主节点宕机,查看系统日志。[2022-03-1718:03:34:595]-master/127.0.0.1:8001用作slave[2022-03-1718:03:34:596]-slaveredis://127.0.0.1:8004删除插槽范围:[[0-5460]][2022-03-1718:03:34:611]-1个连接初始化为/127.0.0.1:8004[2022-03-1718:03:34:639]-/127.0.0.1:8001master和相关的slaves:[addr=redis://127.0.0.1:8004]removed[2022-03-1718:03:34:641]-为/127.0.0.1初始化了24个连接:8004[2022-03-1718:03:34:655]-为/127.0.0.1:8004[2022-03-1718:03:34:678]初始化了1个连接-master:redis://127.0。为插槽范围添加了0.1:8004:[[0-5460]][2022-03-1718:03:34:678]-24个连接初始化为/127.0.0.1:8004从打印的日志中,客户端已经感知到主备倒换,初始化24个连接到最新的主节点127.0.0.1:8004。然后,回到业务功能,读写缓存数据也是最新主节点的操作。还有另一种解决方案:刷新节点拓扑视图。Lettuce官方说明:https://github.com/lettuce-io/lettuce-core/wiki/Redis-Cluster#user-content-refreshing-the-cluster-topology-view。Lettuce处理Moved和Ask永久重定向,由于命令重定向,必须刷新节点拓扑视图。默认情况下禁用自适应更新和定期更新。解决方案:调用RedisClusterClient.reloadPartitions。背景根据时间间隔定期刷新。背景根据持续断开和移动和重定向自适应更新。编写代码@Bean(destroyMethod="destroy")publicLettuceConnectionFactorylettuceConnectionFactory(){//启用自适应集群拓扑刷新和周期性拓扑刷新ClusterTopologyRefreshOptionsclusterTopologyRefreshOptions=ClusterTopologyRefreshOptions.builder()//启用自适应刷新。否则更换Redis集群后连接会异常。enableAllAdaptiveRefreshTriggers()//自适应刷新超时(默认30秒).adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))//启用周期性刷新.enablePeriodicRefresh(Duration.ofSeconds(20)).build();ClientOptionsclientOptions=ClusterClientOptions.builder().topologyRefreshOptions(clusterTopologyRefreshOptions).build();LettuceClientConfigurationclientConfig=LettucePoolingClientConfiguration.builder().poolConfig(genericObjectPoolConfig(redisProperties.getJedis().getPool()))。clientOptions(clientOptions).commandTimeout(redisProperties.getTimeout())//默认RedisURI.DEFAULT_TIMEOUT60.build();ListclusterNodes=redisProperties.getCluster().getNodes();Setnodes=newHashSet();clusterNodes.forEach(地址->nodes.add(新的RedisNode(地址.split(":")[0].trim(),Integer.valueOf(address.split(":")[1]))));RedisClusterConfigurationclusterConfiguration=newRedisClusterConfiguration();clusterConfiguration.setClusterNodes(节点);clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());LettuceConnectionFactorylettuceConnectionFactory=newLettuceConnectionFactory(clusterConfiguration,clientConfig);//是否允许多个线程同时操作一个缓存连接,默认true,false每次操作都会创建一个新连接//lettuceConnectionFactory.setShareNativeConnection(false);//重置底层共享连接,下次访问时初始化//lettuceConnectionFactory.resetConnection();返回生菜连接工厂;}