springboot多实例reids,使用lettuce线程池

本文介绍如何在 Spring Boot 项目中配置并使用多个 Redis 实例,涵盖依赖引入、配置文件设置、代码实现等方面,并提供解决方案以应对 Redis 集群节点故障导致的连接问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


 

因项目实际需要加载多个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的哨兵模式配置集群,需要解决上述问题;解决办法如下:

  1. 升级到SpringBoot2.3.0或以上版本。并添加如下配置项:
    ​
    redis: 
      timeout: 60
      lettuce: 
         cluster: 
             refresh: 
                 period: 60s
                 adaptive: true
    
    ​
  2. 配置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;
    }
    
  3. 将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>
    

Spring Boot中,可以通过配置文件或编程方式来设置Redis线程池。以下是通过编程方式实现整合Redis并设置Redis线程池的步骤: 1. 首先,在你的Spring Boot项目中,添加对Redis和Lettuce(Redis的Java客户端)的依赖。可以在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </dependency> ``` 2. 在application.properties或application.yml配置文件中添加Redis相关配置,包括主机名、端口号、密码等。例如: ```yaml spring.redis.host=localhost spring.redis.port=6379 spring.redis.password= ``` 3. 创建Redis连接工厂类,可以使用LettuceConnectionFactory。在该类中,你可以设置与Redis连接相关的属性,包括连接池配置。下面是一个示例: ```java import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @Configuration public class RedisConfig { @Value("${spring.redis.host}") private String redisHost; @Value("${spring.redis.port}") private int redisPort; @Value("${spring.redis.password}") private String redisPassword; @Bean public LettuceConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisHost, redisPort); config.setPassword(redisPassword); return new LettuceConnectionFactory(config); } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); return redisTemplate; } } ``` 在上述示例中,LettuceConnectionFactory被创建为一个Bean,并通过RedisStandaloneConfiguration设置了连接的主机名、端口号和密码。然后,将该连接工厂设置到RedisTemplate中。 4. 在需要使用Redis的地方,注入RedisTemplate,并调用其相关方法来操作Redis。例如: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service public class MyService { private final RedisTemplate<String, Object> redisTemplate; @Autowired public MyService(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } public void saveData(String key, Object value) { redisTemplate.opsForValue().set(key, value); } public Object getData(String key) { return redisTemplate.opsForValue().get(key); } } ``` 上述示例中的MyService类注入了RedisTemplate,并通过redisTemplate实现了保存数据和获取数据的方法。 这样,你就可以使用Spring Boot整合Redis并设置Redis线程池了。注意,在实际应用中,你可能还需要做一些其他配置和异常处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pan4609

整理不易,谢谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值