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 具体配置
@Autowired
private 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 具体配置
@Autowired
private 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 {
@Autowired
private 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
如果有时间,会在下篇文章中实现。