因项目实际需要加载多个redis实例,特此总结网上相关知识,项目使用springboot2.1.10.RELEASE,springboot2.0版本以上,默认使用lettuce线程池;
一、依赖
项目使用springboot2.1.10.RELEASE
版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--线程池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
二、配置文件
# Redis配置(测试)
redis:
lettuce:
pool:
max-wait-millis: 100 #连接池最大阻塞等待时间(使用负值表示没有限制)
max-active: 6000 # 连接池最大连接数(使用负值表示没有限制)
max-idle: 100 # 连接池中的最大空闲连接
min-idle: 50
redis-onedb: #当前项目默认使用redis
database: 10
hostName: 127.0.0.1
port: 6379
timeout: 5000
password: 123456
redis-twodb: #其他项目redis地址
database: 7
hostName: 127.0.0.1
port: 6379
timeout: 5000
password: 123456
三、代码实现
@Configuration
public class RedisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
@Scope(value = "prototype")
public GenericObjectPoolConfig redisPool(){
return new GenericObjectPoolConfig();
}
@Bean
@ConfigurationProperties(prefix = "spring.redis.redis-onedb")
public RedisStandaloneConfiguration redisConfigA(){
return new RedisStandaloneConfiguration();
}
@Primary
@Bean
public LettuceConnectionFactory factoryA(GenericObjectPoolConfig config, RedisStandaloneConfiguration redisConfigA){
return new LettuceConnectionFactory(redisConfigA, getLettuceClientConfiguration(config));
}
@Bean(name = "oneRedisTemplate")
public StringRedisTemplate redisOneTemplate(@Qualifier("factoryA") LettuceConnectionFactory factoryA){
StringRedisTemplate template = getRedisTemplate();
template.setConnectionFactory(factoryA);
return template;
}
@Bean
@ConfigurationProperties(prefix = "spring.redis.redis-twodb")
public RedisStandaloneConfiguration redisConfigB(){
return new RedisStandaloneConfiguration();
}
@Bean
public LettuceConnectionFactory factoryB(GenericObjectPoolConfig config, RedisStandaloneConfiguration redisConfigB){
return new LettuceConnectionFactory(redisConfigB, getLettuceClientConfiguration(config));
}
@Bean(name = "twoRedisTemplate")
public StringRedisTemplate redisTwoTemplate(@Qualifier("factoryB")LettuceConnectionFactory factoryB){
StringRedisTemplate template = getRedisTemplate();
template.setConnectionFactory(factoryB);
return template;
}
private StringRedisTemplate getRedisTemplate(){
StringRedisTemplate template = new StringRedisTemplate();
template.setValueSerializer(new GenericFastJsonRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
/**
* 配置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())
.build();
四.测试
//选择注入redis
@Resource(name = "twoRedisTemplate")
private StringRedisTemplate twoRedisTemplate;
@Resource(name = "oneRedisTemplate")
private StringRedisTemplate oneRedisTemplate;
twoRedisTemplate.opsForValue().set("123","321",1000, TimeUnit.SECONDS);
oneRedisTemplate.opsForValue().set("123","321",1000, TimeUnit.SECONDS);
五、遗留问题
SpringBoot2.x开始默认使用的Redis客户端由Jedis变成了Lettuce,但当Redis集群中某个节点挂掉后,Lettuce将无法继续操作Redis,原因在此时Lettuce使用的仍然是有问题的连接信息。
实际上,Lettuce支持redis 集群拓扑动态刷新,但是默认并没有开启,SpringBoot在集成Lettuce时默认也没有开启。并且在SpringBoot2.3.0之前,是没有配置项设置Lettuce自动刷新拓扑的;
解决办法:
如果只使用redis的单节点,则不存在上述的问题,本地以测试,如果使用redis的哨兵模式配置集群,需要解决上述问题;解决办法如下:
- 升级到SpringBoot2.3.0或以上版本。并添加如下配置项:
redis: timeout: 60 lettuce: cluster: refresh: period: 60s adaptive: true
-
配置LettuceConnectionFactory ,设置拓扑刷新策略
@Bean public DefaultClientResources lettuceClientResources() { return DefaultClientResources.create(); } @Bean public LettuceConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties, ClientResources clientResources) { ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(Duration.ofSeconds(30)) //按照周期刷新拓扑 .enableAllAdaptiveRefreshTriggers() //根据事件刷新拓扑 .build(); ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder() //redis命令超时时间,超时后才会使用新的拓扑信息重新建立连接 .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(10))) .topologyRefreshOptions(topologyRefreshOptions) .build(); LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder() .clientResources(clientResources) .clientOptions(clusterClientOptions) .build(); RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(redisProperties.getCluster().getNodes()); clusterConfig.setMaxRedirects(redisProperties.getCluster().getMaxRedirects()); clusterConfig.setPassword(RedisPassword.of(redisProperties.getPassword())); LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(clusterConfig, clientConfiguration); return lettuceConnectionFactory; }
-
将spring-boot-starter-data-redis依赖的Lettuce,修改成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>