Spring Boot集成Redis集群,使用lettuce连接Cluster集群实例。
redis其中一个节点挂了之后,springboot集成redis集群配置信息没有及时刷新,出现读取操作报错。
- java.lang.IllegalArgumentException: Connection to 127.0.0.1:6379 not allowed. This connection point is not known in the cluster view
- exceptionStackTrace
- io.lettuce.core.cluster.PooledClusterConnectionProvider.getConnectionAsync(PooledClusterConnectionProvider.java:359)
- io.lettuce.core.cluster.ClusterDistributionChannelWriter.write(ClusterDistributionChannelWriter.java:93)
- io.lettuce.core.cluster.ClusterCommand.complete(ClusterCommand.java:56)
- io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:563)
- io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:516)
lettuce默认是没有开始拓扑更新及读写分离导致的
这里分为几种情况:
- springboot 1.x之前版本默认使用jedis,无需要手动开启刷新
- springboot 2.x默认为Lettuce,需要代码设置开启刷新节点拓扑策略
- springboot 2.3.0开始,支持集群拓扑刷新功能,属性配置开启即可
springboot1.x之前版本默认使用jedis,无需手动开启动态刷新。
springboot2.0-2.3版本默认使用lettuce,默认不支持属性配置集群拓扑刷新。使用lettuce,需要增加配置类,需要手动开启刷新。
配置类如下:
- package com.test.config;
-
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import io.lettuce.core.cluster.ClusterClientOptions;
- import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
- import lombok.extern.java.Log;
- 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.cache.annotation.EnableCaching;
- import org.springframework.cache.interceptor.KeyGenerator;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.connection.RedisClusterConfiguration;
- import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
- 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.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.RedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
-
- import java.lang.reflect.Method;
- import java.time.Duration;
-
-
- @Log
- @Configuration
- @EnableCaching // 开启缓存支持
- public class RedisConfig {
-
- @Autowired
- private RedisProperties redisProperties;
-
- @Bean(destroyMethod = "destroy") //销毁这个bean之前调用这个destroy回调方法释放资源
- public LettuceConnectionFactory redisConnectionFactory() {
- // redis单节点
- if (null == redisProperties.getCluster() || null == redisProperties.getCluster().getNodes()) {
- RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisProperties.getHost(),
- redisProperties.getPort());
- configuration.setPassword(redisProperties.getPassword()); //RedisPassword.of(redisProperties.getPassword())
- configuration.setDatabase(redisProperties.getDatabase());
- return new LettuceConnectionFactory(configuration);
- }
-
- // redis集群
- RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
- redisClusterConfiguration.setPassword(redisProperties.getPassword()); //RedisPassword.of(redisProperties.getPassword())
- redisClusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
-
- GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
- genericObjectPoolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
- genericObjectPoolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
- genericObjectPoolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
- genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().getSeconds());
-
- // 支持自适应集群拓扑刷新和动态刷新源
- ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
- .enableAllAdaptiveRefreshTriggers()
- // 开启自适应刷新
- .enableAdaptiveRefreshTrigger()
- // 开启定时刷新
- .enablePeriodicRefresh(Duration.ofSeconds(5))
- .build();
-
- ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
- .topologyRefreshOptions(clusterTopologyRefreshOptions).build();
-
- LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
- .poolConfig(genericObjectPoolConfig) //如果使用默认配置可以注释genericObjectPoolConfig
- // .readFrom(ReadFrom.SLAVE_PREFERRED) //读写分离:主写从读模式配置
- .clientOptions(clusterClientOptions).build();
-
- LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
- lettuceConnectionFactory.setShareNativeConnection(false);// 是否允许多个线程操作共用同一个缓存连接,默认 true,false 时每个操作都将开辟新的连接
- lettuceConnectionFactory.resetConnection();// 重置底层共享连接, 在接下来的访问时初始化
- return lettuceConnectionFactory;
- }
-
- /**
- * RedisTemplate配置
- */
- @Bean
- public RedisTemplate
- RedisTemplate
- redisTemplate.setConnectionFactory(factory);
-
- // 使用注解@Bean返回RedisTemplate的时候,同时配置hashkey和hashValue的序列虎方式
- // key采用String的序列化方式
- redisTemplate.setKeySerializer(keySerializer());
- // value使用jackson序列化方式
- redisTemplate.setValueSerializer(valueSerializer());
-
- // hash的key采用String的序列化方式
- redisTemplate.setHashKeySerializer(keySerializer());
- // hash的value使用jackson序列化方式
- redisTemplate.setHashValueSerializer(valueSerializer());
-
- /**必须执行这个函数,初始化RedisTemplate*/
- // 需要先调用afterPropertiesSet方法,此方法是应该是初始化参数和初始化工作。
- redisTemplate.afterPropertiesSet();
- log.info("序列化完成!");
- return redisTemplate;
- }
-
-
-
-
- @Bean
- public KeyGenerator keyGenerator() {
- return new KeyGenerator() {
- @Override
- public Object generate(Object target, Method method, Object... params) {
- StringBuffer sb = new StringBuffer();
- sb.append(target.getClass().getName());
- sb.append(method.getName());
- for (Object obj : params) {
- sb.append(obj.toString());
- }
- return sb.toString();
- }
- };
- }
-
-
-
- /**
- * key键序列化方式
- *
- * @return RedisSerializer
- */
- private RedisSerializer
keySerializer() { - return new StringRedisSerializer();
- }
-
-
- /**
- * value值序列化方式
- *
- * @return
- */
- private Jackson2JsonRedisSerializer valueSerializer() {
- Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- return jackson2JsonRedisSerializer;
- }
- }
配置文件:
- #Redis Configuration
- spring.redis.cluster.max-redirects=10
- spring.redis.cluster.nodes=127.0.0.1:8001,127.0.0.1:8002
- spring.redis.timeout=60000ms
- spring.redis.password=
- spring.redis.lettuce.pool.max-active=10
- spring.redis.lettuce.pool.max-idle=8
- spring.redis.lettuce.pool.min-idle=0
- spring.redis.lettuce.pool.max-wait=-1ms
注意:注入LettuceConnectionFactory后,一定要记得注入RedisTemplate,并 redisTemplate.setConnectionFactory(factory);
apache commons-pool2 包提供了一个通用的对象池技术的实现。可以很方便的基于它来实现自己的对象池,比如 DBCP 和 Jedis 他们的内部对象池的实现就是依赖于 commons-pool2 。
-
org.apache.commons -
commons-pool2 -
2.8.0
springboot2.3之后版本默认使用lettuce,默认支持属性配置开启集群拓扑刷新,其解决方案:属性配置开启即可。
- spring.redis.lettuce.cluster.refresh.adaptive= true
- spring.redis.lettuce.cluster.refresh.period=30000 # 30秒自动刷新一次
关联文章:Spring Boot集成Redis集群报错UnsupportedOperationException-CSDN博客