首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Redis集群Lettuce主从切换问题

一、问题描述

Redis Cluster集群,当master宕机,主从切换,客户端报错 timed out

二、原因

SpringBoot2.X版本开始Redis默认的连接池都是采用的Lettuce。当节点发生改变后,Letture默认是不会刷新节点拓扑的。

三、解决方案3.1 方案一:把lettuce换成jedis

只需要在pom.xml里调整一下依赖的引用。

3.2 方案二:刷新节点拓扑视图

Redis节点异常,服务端的Redis集群拓扑被刷新了,Java程序没有获取到新的拓扑。

Lettuce官方文档中关于Redis Cluster的相关说明:Lettuce处理Moved和Ask永久重定向,由于命令重定向,你必须刷新节点拓扑视图。而自适应拓扑刷新(Adaptive updates)与定时拓扑刷新(Periodic updates)是默认关闭的,可以通过如下代码打开。

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

修改代码如下

package com.montnets.common.redis;

import io.lettuce.core.ClientOptions;import io.lettuce.core.cluster.ClusterClientOptions;import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.context.annotation.Bean;import org.springframework.data.redis.connection.RedisClusterConfiguration;import org.springframework.data.redis.connection.RedisNode;import org.springframework.data.redis.connection.RedisPassword;import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;import org.springframework.stereotype.Component;

import java.time.Duration;import java.util.HashSet;import java.util.List;import java.util.Set;

@Componentpublic class RedisPoolConfig {

@Autowired private RedisProperties redisProperties;

public GenericObjectPoolConfig<?> genericObjectPoolConfig(RedisProperties.Pool properties) { GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>(); config.setMaxTotal(properties.getMaxActive()); config.setMaxIdle(properties.getMaxIdle()); config.setMinIdle(properties.getMinIdle()); if (properties.getTimeBetweenEvictionRuns() != null) { config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns().toMillis()); } if (properties.getMaxWait() != null) { config.setMaxWaitMillis(properties.getMaxWait().toMillis()); } return config; }

@Bean(destroyMethod = "destroy") public LettuceConnectionFactory lettuceConnectionFactory() {

//开启 自适应集群拓扑刷新和周期拓扑刷新 ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder() // 开启全部自适应刷新 .enableAllAdaptiveRefreshTriggers() // 开启自适应刷新,自适应刷新不开启,Redis集群变更时将会导致连接异常 // 自适应刷新超时时间(默认30秒) .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30)) //默认关闭开启后时间为30秒 // 开周期刷新 .enablePeriodicRefresh(Duration.ofSeconds(20)) // 默认关闭开启后时间为60秒 ClusterTopologyRefreshOptions.DEFAULT_REFRESH_PERIOD 60 .enablePeriodicRefresh(Duration.ofSeconds(2)) = .enablePeriodicRefresh().refreshPeriod(Duration.ofSeconds(2)) .build();

// https://github.com/lettuce-io/lettuce-core/wiki/Client-Options ClientOptions clientOptions = ClusterClientOptions.builder() .topologyRefreshOptions(clusterTopologyRefreshOptions) .build();

LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder() .poolConfig(genericObjectPoolConfig(redisProperties.getJedis().getPool())) //.readFrom(ReadFrom.MASTER_PREFERRED) .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); // lettuceConnectionFactory.setShareNativeConnection(false); //是否允许多个线程操作共用同一个缓存连接,默认true,false时每个操作都将开辟新的连接 // lettuceConnectionFactory.resetConnection(); // 重置底层共享连接, 在接下来的访问时初始化 return lettuceConnectionFactory; }}

  • 发表于:
  • 原文链接https://page.om.qq.com/page/Ob2DyspVsXbUMdn-HXZrKjbQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券