做毕设的时候,使用到Lettuce连接redis,一段时间后不操作,再去操作redis,会报连接超时错误,在其重连后又可使用。
原因是:Lettuce 自适应拓扑刷新(Adaptive updates)与定时拓扑刷新(Periodic updates) 是默认关闭的导致问题的出现
1、重写连接工厂实例,更改其LettuceClientConfiguration 为开启拓扑更新
@Configuration
public class RedisConfig {
@Autowired
private RedisProperties redisProperties;
//这是固定的模板
//自己定义了一个RedisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(@Qualifier("lettuceConnectionFactoryUvPv") RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//Json序列化配置
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(om.getPolymorphicTypeValidator());
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//解决序列化问题
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jackson2JsonRedisSerializer.setObjectMapper(om);
//String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
/**
* 为RedisTemplate配置Redis连接工厂实现
* LettuceConnectionFactory实现了RedisConnectionFactory接口
* UVPV用Redis
*
* @return 返回LettuceConnectionFactory
*/
@Bean(destroyMethod = "destroy")
//这里要注意的是,在构建LettuceConnectionFactory 时,如果不使用内置的destroyMethod,可能会导致Redis连接早于其它Bean被销毁
public LettuceConnectionFactory lettuceConnectionFactoryUvPv() throws Exception {
// List<String> clusterNodes = redisProperties.getCluster().getNodes();
// Set<RedisNode> nodes = new HashSet<>();
// clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.parseInt(address.split(":")[1]))));
// RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
// clusterConfiguration.setClusterNodes(nodes);
// clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
// clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
//我使用的是单机redis,集群使用上面注释的代码
Set<RedisNode> nodes = new HashSet<>();
nodes.add(new RedisNode(redisProperties.getHost(), redisProperties.getPort()));
RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisProperties.getHost());
redisStandaloneConfiguration.setPassword(redisProperties.getPassword());
redisStandaloneConfiguration.setDatabase(redisProperties.getDatabase());
redisStandaloneConfiguration.setPort(redisProperties.getPort());
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
poolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
poolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
return new LettuceConnectionFactory(redisStandaloneConfiguration, getLettuceClientConfiguration(poolConfig));
}
/**
* 配置LettuceClientConfiguration 包括线程池配置和安全项配置
*
* @param genericObjectPoolConfig common-pool2线程池
* @return lettuceClientConfiguration
*/
private LettuceClientConfiguration getLettuceClientConfiguration(GenericObjectPoolConfig genericObjectPoolConfig) {
/*
ClusterTopologyRefreshOptions配置用于开启自适应刷新和定时刷新。如自适应刷新不开启,Redis集群变更时将会导致连接异常!
*/
ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
//开启自适应刷新
//.enableAdaptiveRefreshTrigger(ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT, ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS)
//开启所有自适应刷新,MOVED,ASK,PERSISTENT都会触发
.enableAllAdaptiveRefreshTriggers()
// 自适应刷新超时时间(默认30秒)
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(25)) //默认关闭开启后时间为30秒
// 开周期刷新
.enablePeriodicRefresh(Duration.ofSeconds(20)) // 默认关闭开启后时间为60秒 ClusterTopologyRefreshOptions.DEFAULT_REFRESH_PERIOD 60 .enablePeriodicRefresh(Duration.ofSeconds(2)) = .enablePeriodicRefresh().refreshPeriod(Duration.ofSeconds(2))
.build();
return LettucePoolingClientConfiguration.builder()
.poolConfig(genericObjectPoolConfig)
.clientOptions(ClusterClientOptions.builder().topologyRefreshOptions(topologyRefreshOptions).build())
//将appID传入连接,方便Redis监控中查看
//.clientName(appName + "_lettuce")
.build();
}
}
2、SpringBoot2.3.x后,可使用配置文件中开启lettuce的拓扑刷新
lettuce:
pool:
max-active: 20
max-wait: -1ms
max-idle: 10
min-idle: 2
cluster:
refresh:
adaptive: true
#20秒自动刷新一次
period: 20
3、更改连接redis的连接方式,使用jedis连接
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
spring:
redis:
jedis:
pool:
max-active: ${redis.config.maxTotal:1024}
max-idle: ${redis.config.maxIdle:50}
min-idle: ${redis.config.minIdle:1}
max-wait: ${redis.config.maxWaitMillis:5000}
#lettuce:
#pool:
#max-active: ${redis.config.maxTotal:1024}
#max-idle: ${redis.config.maxIdle:50}
#min-idle: ${redis.config.minIdle:1}
#max-wait: ${redis.config.maxWaitMillis:5000}
SpringBoot整合redis使用Lettuce客户端超时问题
原文:https://www.cnblogs.com/shouyaya/p/14607270.html