【SpringCloudGateway】SpringCloudGateway配置多套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; }
/** * @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; }