1.前言
本文为大家提供一个 redis 配置多数据源的实现方案,在实际项目中遇到,分享给大家。后续如果有时间会写一个升级版本,升级方向在第5点。
2.git 示例地址
git 仓库地址:https://github.com/huajiexiewenfeng/redis-multi-spring/tree/master/redis-multi-spring
不同的 spring boot 版本,对应的写法可能有小的区别,但思想不变
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.0.1.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.0.1.RELEASE</version></dependency>
3.需求分析
对于 redis 配置多数据源的需求,至少可以分析出以下需求和实现点
- 在同一个项目中,可以操作多个 redis 服务实例
- 通过不同的 redisTemplate 可以调用不同的 redis 实例服务
- Spring @Qualifier 来实现
- 如果原项目已经引入spring-redis,不能影响 Spring boot 里面默认配置的 redis 自动配置服务
- org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
- 要与 RedisAutoConfiguration 里面的 spring bean 完全隔离
- 因为 host port 等重要参数都在 RedisConnectionFactory 中,所以 RedisConnectionFactory 我们需要配置多个
4.代码实现
不能影响 Spring boot 里面默认配置的 redis 自动配置服务,以 JedisConnectionConfiguration 自动配置实现为例。
- @ConditionalOnMissingBean({RedisConnectionFactory.class}) 表示如果 spring 容器中存在 RedisConnectionFactory 的具体实现,那么该自动配置 bean 方法不会生效;所以在我们后面的实现中要避免这种写法。
4.1 第一个 RedisTemplate 配置
需要特别注意的是,在某些 spring boot 版本里面,如果不写或者少写该配置,使用过程中会报错
- template.afterPropertiesSet();
- jedisConnectionFactory.afterPropertiesSet();
为了避免 getJedisConnectionFactory 方法影响 spring redis 默认配置,不能增加 @Bean 注解的方式,用 name 属性区分也不行。
4.1.1 RedisOneProperties Properties 类
@Configuration
@ConfigurationProperties(prefix = "multi.redis.one")
@Data
public class RedisOneProperties {private String host;private int port;private int database = 1;private String password;}
4.1.2 RedisMultiConfiguration 具体配置
@Autowiredprivate RedisOneProperties properties;// 第一个Redis服务器的配置@Bean("oneRedisTemplate")public RedisTemplate<String, Object> oneRedisTemplate() {JedisConnectionFactory jedisConnectionFactory = this.getJedisConnectionFactory();RedisTemplate<String, Object> template = new RedisTemplate<>();// 设置key的序列化方式StringRedisSerializer keySerializer = new StringRedisSerializer();template.setConnectionFactory(jedisConnectionFactory);template.setKeySerializer(keySerializer);// 设置value的序列化方式Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是无论什么都可以序列化om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 启用DefaultTyping,方便我们反序列化时知道对象的类型om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);valueSerializer.setObjectMapper(om);template.setValueSerializer(valueSerializer);// 设置Hash的key和value序列化方式template.setHashKeySerializer(keySerializer);template.setHashValueSerializer(valueSerializer);// 设置value的泛型类型,这样在存取的时候才会序列化和反序列化成设置的对象类型// 注意:这里只是设置了value的泛型,key还是String类型template.afterPropertiesSet();return template;}private JedisConnectionFactory getJedisConnectionFactory() {JedisPoolConfig poolConfig = new JedisPoolConfig();// 设置连接池参数,例如最大连接数、最大空闲连接数等poolConfig.setMaxTotal(100);poolConfig.setMaxIdle(30);poolConfig.setMinIdle(10);JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);jedisConnectionFactory.setHostName(properties.getHost());jedisConnectionFactory.setPort(properties.getPort());jedisConnectionFactory.setDatabase(properties.getDatabase());jedisConnectionFactory.setPassword(properties.getPassword());jedisConnectionFactory.afterPropertiesSet();return jedisConnectionFactory;}
4.2 第二个 RedisTemplate 配置
以此类推
4.2.1 RedisTwoProperties Properties 类
@Configuration
@ConfigurationProperties(prefix = "multi.redis.two")
@Data
public class RedisTwoProperties {private String host;private int port;private int database = 1;private String password;}
4.2.2 RedisMultiConfiguration 具体配置
@Autowiredprivate RedisTwoProperties propertiesTwo;@Bean("twoRedisTemplate")public RedisTemplate<String, Object> twoRedisTemplate() {JedisConnectionFactory jedisConnectionFactory = this.getJedisConnectionFactoryTwo();RedisTemplate<String, Object> template = new RedisTemplate<>();// 设置key的序列化方式StringRedisSerializer keySerializer = new StringRedisSerializer();template.setConnectionFactory(jedisConnectionFactory);template.setKeySerializer(keySerializer);// 设置value的序列化方式Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是无论什么都可以序列化om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 启用DefaultTyping,方便我们反序列化时知道对象的类型om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);valueSerializer.setObjectMapper(om);template.setValueSerializer(valueSerializer);// 设置Hash的key和value序列化方式template.setHashKeySerializer(keySerializer);template.setHashValueSerializer(valueSerializer);// 设置value的泛型类型,这样在存取的时候才会序列化和反序列化成设置的对象类型// 注意:这里只是设置了value的泛型,key还是String类型template.afterPropertiesSet();return template;}private JedisConnectionFactory getJedisConnectionFactoryTwo() {JedisPoolConfig poolConfig = new JedisPoolConfig();// 设置连接池参数,例如最大连接数、最大空闲连接数等poolConfig.setMaxTotal(100);poolConfig.setMaxIdle(30);poolConfig.setMinIdle(10);JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);jedisConnectionFactory.setHostName(propertiesTwo.getHost());jedisConnectionFactory.setPort(propertiesTwo.getPort());jedisConnectionFactory.setDatabase(propertiesTwo.getDatabase());jedisConnectionFactory.setPassword(propertiesTwo.getPassword());jedisConnectionFactory.afterPropertiesSet();return jedisConnectionFactory;}
4.3 application.properties 配置文件
spring.application.name=${APPLICATION_NAME:redis-multiple}
server.port=${SERVER_PORT:22216}# spring 默认配置
spring.redis.database=${REDIS_DB_INDEX:1}
spring.redis.flushdb=${REDIS_FLUSHDB:false}
spring.redis.host=${REDIS_HOST:127.0.0.1}
spring.redis.port=${REDIS_PORT:6379}
spring.redis.password=123456#第一个 redis 实例配置
multi.redis.one.database=${REDIS_DB_INDEX:2}
multi.redis.one.flushdb=${REDIS_FLUSHDB:false}
multi.redis.one.host=${REDIS_HOST:127.0.0.1}
multi.redis.one.port=${REDIS_PORT:6379}
multi.redis.one.password=123456#第二个 redis 实例配置
multi.redis.two.database=${REDIS_DB_INDEX:3}
multi.redis.two.flushdb=${REDIS_FLUSHDB:false}
multi.redis.two.host=${REDIS_HOST:127.0.0.1}
multi.redis.two.port=${REDIS_PORT:6379}
multi.redis.two.password=123456
4.4 测试 Demo
@RestController
public class TestController {@Autowiredprivate RedisTemplate<String,String> redisTemplate;@Autowired@Qualifier("oneRedisTemplate")private RedisTemplate<String, Object> redisTemplateOne;@Autowired@Qualifier("twoRedisTemplate")private RedisTemplate<String, Object> redisTemplateTwo;@GetMapping("/test/redis/add")public void profileDetails() {redisTemplate.opsForValue().set("test1", "1");redisTemplateOne.opsForValue().set("test2", 2);redisTemplateTwo.opsForValue().set("test3", 3);}}
浏览器输入
http://127.0.0.1:22216/test/redis/add
执行结果如下:
db1
db2
db3
5.版本升级想法
1.不需要每一个配置都写一个 Properties 类
2.不需要每一个配置都写一个 RedisTemplate 配置
@Bean("oneRedisTemplate")public RedisTemplate<String, Object> oneRedisTemplate() {...}@Bean("twoRedisTemplate")public RedisTemplate<String, Object> twoRedisTemplate() {...}
3.使用方法可以与 properties 里面的配置直接对应,比如 oneRedisTemplate
@Autowired@Qualifier("oneRedisTemplate")private RedisTemplate<String, Object> redisTemplateOne;
对应 application.properties 中的 multi.redis.one 前缀
multi.redis.one.database=${REDIS_DB_INDEX:2}
multi.redis.one.flushdb=${REDIS_FLUSHDB:false}
multi.redis.one.host=${REDIS_HOST:127.0.0.1}
multi.redis.one.port=${REDIS_PORT:6379}
multi.redis.one.password=123456
如果有时间,会在下篇文章中实现。