【SpringCloudGateway】SpringCloudGateway配置多套Redis数据源

一.背景

  SpringCloudGateway增加基于Redis的限流功能,但由于认证鉴权功能也是在网关连接Redis完成,本着上级上单一职责的原则,尽量将认证鉴权Redis与限流使用的Redis独立开来,在网关配置了多套Redis数据源用于实现不同的功能,总结和记录在配置过程的经验

二.想法

  由于需要使用两套不同的数据源,项目中使用到了RedisTemplate,因此需要配置两套不同的RedisTemplate Bean对象,并且需要保证Bean name不冲突,能被实例化到Spring IOC 容器中。

三.实现

  这里将第一套Redis数据源称为One,用于限流拦截,使用spring.redis.这个默认的配置,将第二套数据源称为Second,用于认证鉴权,使用新的yaml配置(second.redis)
      由于SpringBoot的思想是约定大于配置,默认的数据源,首先这里需要手动声明第二套Redis的Bean对象和Redis连接工厂对象
/**
 * 注意:
 * 第一套Redis与第二套Redis隔离
 * 第一套Redis配置用于限流,第二套Redis配置用于Token认证(和用户服务Redis保持一致)
 *
 * @author Sam.yang
 * @since 2023/12/15 21:41
 */
@Configuration
public class RedisConfig {

    @Autowired
    private SecondRedisConnectConfig secondRedisConnectConfig;
    @Autowired
    private SecondRedisPoolConnectConfig secondRedisPoolConnectConfig;
    @Autowired
    private OneRedisConnectConfig oneRedisConnectConfig;
    @Autowired
    private OneRedisPoolConnectConfig oneRedisPoolConnectConfig;

    @Bean(value = "secondRedisTemplate")
    public RedisTemplate<String, Object> secondRedisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(secondRedisConnectionFactory());
        return redisTemplate;
    }

    @Bean(name = "secondStringRedisTemplate")
    public StringRedisTemplate secondStringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(secondRedisConnectionFactory());
        return stringRedisTemplate;
    }

    /**
     * 配置第二套Redis连接工厂
     *
     * @return {@link RedisConnectionFactory}
     */
    @Bean(value = "secondRedisConnectionFactory")
    public RedisConnectionFactory secondRedisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(secondRedisConnectConfig.getHost());
        redisStandaloneConfiguration.setPort(secondRedisConnectConfig.getPort());
        redisStandaloneConfiguration.setDatabase(secondRedisConnectConfig.getDatabase());
        redisStandaloneConfiguration.setPassword(secondRedisConnectConfig.getPassword());

        //配置连接池
        GenericObjectPoolConfig secondGenericObjectPoolConfig = new GenericObjectPoolConfig();
        secondGenericObjectPoolConfig.setMaxTotal(secondRedisPoolConnectConfig.getMaxActive());
        secondGenericObjectPoolConfig.setMaxIdle(secondRedisPoolConnectConfig.getMaxIdle());
        secondGenericObjectPoolConfig.setMinIdle(secondRedisPoolConnectConfig.getMinIdle());
        secondGenericObjectPoolConfig.setMaxWaitMillis(secondRedisPoolConnectConfig.getMaxWait());

        JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
                .usePooling().poolConfig(secondGenericObjectPoolConfig).and()
                .connectTimeout(Duration.ofMillis(secondRedisConnectConfig.getConnectTimeout()))
                .readTimeout(Duration.ofMillis(secondRedisConnectConfig.getReadTimeout()))
                .build();

        return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
    }


    @Bean
    @Primary
    public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(oneRedisConnectConfig.getHost());
        redisStandaloneConfiguration.setPort(oneRedisConnectConfig.getPort());
        redisStandaloneConfiguration.setDatabase(oneRedisConnectConfig.getDatabase());
        redisStandaloneConfiguration.setPassword(oneRedisConnectConfig.getPassword());

        //配置连接池
        GenericObjectPoolConfig secondGenericObjectPoolConfig = new GenericObjectPoolConfig();
        secondGenericObjectPoolConfig.setMaxTotal(oneRedisPoolConnectConfig.getMaxActive());
        secondGenericObjectPoolConfig.setMaxIdle(oneRedisPoolConnectConfig.getMaxIdle());
        secondGenericObjectPoolConfig.setMinIdle(oneRedisPoolConnectConfig.getMinIdle());
        secondGenericObjectPoolConfig.setMaxWaitMillis(oneRedisPoolConnectConfig.getMaxWait());

        LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
                .poolConfig(secondGenericObjectPoolConfig)
                .commandTimeout(Duration.ofMillis(oneRedisConnectConfig.getConnectTimeout()))
                .shutdownTimeout(Duration.ofMillis(oneRedisConnectConfig.getReadTimeout()))
                .build();

        return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration);
    }

    @Bean
    @Primary
    public ReactiveRedisTemplate<String, String> reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
        return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string());
    }

  注意:这里使用@Primary注解是目的是为了项目启动不报错,(项目中可能有部分Bean对象依赖了RedisConnectionFactory 或者RedisTemplate,这里主要是声明存在多个同类型Bean实例时,按类型注入取哪个注解)

  如果不这么处理,就会出现类似以下报错

Parameter 0 of method diyRedisRateLimiter in com.leigod.gateway.base.configure.RateLimiterConfig required a single bean, but 2 were found:
    - reactiveRedisTemplate: defined by method 'reactiveRedisTemplate' in class path resource [com/leigod/gateway/base/configure/RedisConfig.class]
    - reactiveStringRedisTemplate: defined by method 'reactiveStringRedisTemplate' in class path resource [org/springframework/boot/autoconfigure/data/redis/RedisReactiveAutoConfiguration.class]

  这里回顾下@Primary注解的作用

  @Primary注解是Spring Framework中的一个注解,用于标记具有多个实例的Bean的主要实例。以下是关于@Primary注解的知识点:
  @Primary注解的作用:@Primary注解用于标记具有相同类型的多个实例中的主要实例。当一个Bean需要注入这种类型的Bean时,Spring会注入使用@Primary注解标记的主要实例。
  @Primary注解的使用:在一个有多个实例的Bean中使用@Primary注解来标记主要实例。可以通过在Bean的类定义或构造函数参数中添加@Primary注解来使用它。
  @Primary注解的优先级:当一个类中有多个具有相同类型的Bean时,可以使用@Primary注解设置主要实例。主要实例优先于其他实例被注入。如果没有使用@Primary注解,则最后一个装配的Bean会被注入。
  @Primary注解的局限性:@Primary注解只适用于Bean的类型,不能用于Bean的名称。因此,如果有多个同一类型的Bean,但它们需要以不同的名称被注入,则无法使用@Primary来确定主要实例。
  @Qualifier和@Primary注解的区别:@Primary注解用于标记具有相同类型的多个实例中的主要实例。而@Qualifier注解用于标记具有特定名称的Bean,以便在多个具有相同类型的Bean中选择特定的实例。
  总之,@Primary注解是Spring Framework中的一个重要注解,用于标记具有多个实例的Bean的主要实例。它可以帮助解决多个同一类型的Bean被注入时的问题。

  对应的第一套和第二套配置配置类如下
  第一套Redis One的配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author Sam.yang
 * @since 2023/12/16 15:04
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class OneRedisConnectConfig {

    /**
     * host
     */
    private String host;
    /**
     * port
     */
    private Integer port;
    /**
     * password
     */
    private String password;
    /**
     * database
     */
    private Integer database;
    /**
     * 连接timeout 毫秒
     */
    private Integer connectTimeout;
    /**
     * 读取timeout 毫秒
     */
    private Integer readTimeout;

}
  第二套Redis Second的配置类

/**
 * @author Sam.yang
 * @since 2023/12/16 14:27
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "second.redis")
public class SecondRedisConnectConfig {

    /**
     * host
     */
    private String host;
    /**
     * port
     */
    private Integer port;
    /**
     * password
     */
    private String password;
    /**
     * database
     */
    private Integer database;
    /**
     * 连接timeout 毫秒
     */
    private Integer connectTimeout;
    /**
     * 读取timeout 毫秒
     */
    private Integer readTimeout;


}

  连接池配置

  第一套Redis和第二套Redis 通常需要声明连接池配置,这里对应的连接池配置如下(可分别声明为jedis或lettuce连接池)

  第一套Redis One的连接池配置(lettuce连接池)

/**
 * max-active: 300
 * max-idle:   100
 * min-idle:   100
 * max-wait: 1000ms
 *
 * @author Sam.yang
 * @since 2023/12/16 14:27
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
public class OneRedisPoolConnectConfig {

    /**
     * max-active: 300
     * 最大活跃连接数
     */
    private Integer maxActive;
    /**
     * max-idle: 100
     * 最大连接数
     */
    private Integer maxIdle;
    /**
     * min-idle: 100
     * 最小连接数
     */
    private Integer minIdle;
    /**
     * max-wait: 1000ms
     * 最大等待时间
     */
    private Integer maxWait;


}

  第二套Redis Second的连接池配置(jedis连接池)

/**
 * max-active: 300
 * max-idle:   100
 * min-idle:   100
 * max-wait: 1000ms
 *
 * @author Sam.yang
 * @since 2023/12/16 14:27
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "second.redis.jedis.pool")
public class SecondRedisPoolConnectConfig {

    /**
     * max-active: 300
     * 最大活跃连接数
     */
    private Integer maxActive;
    /**
     * max-idle: 100
     * 最大连接数
     */
    private Integer maxIdle;
    /**
     * min-idle: 100
     * 最小连接数
     */
    private Integer minIdle;
    /**
     * max-wait: 1000ms
     * 最大等待时间
     */
    private Integer maxWait;


}

 

 
posted @ 2023-12-18 00:57  听风是雨  阅读(493)  评论(0编辑  收藏  举报
/* 看板娘 */