Redis 多数据源 Spring Boot 实现

1.前言

本文为大家提供一个 redis 配置多数据源的实现方案,在实际项目中遇到,分享给大家。后续如果有时间会写一个升级版本,升级方向在第5点。

2.git 示例地址

git 仓库地址:https://github.com/huajiexiewenfeng/redis-multi-spring/tree/master/redis-multi-spring

不同的 spring boot 版本,对应的写法可能有小的区别,但思想不变

xml 复制代码
        <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 类
java 复制代码
@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 具体配置
java 复制代码
    @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 类
java 复制代码
@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 具体配置
java 复制代码
    @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 配置文件

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

java 复制代码
@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);
    }

}

浏览器输入

html 复制代码
http://127.0.0.1:22216/test/redis/add

执行结果如下:

db1

db2

db3

5.版本升级想法

1.不需要每一个配置都写一个 Properties 类

2.不需要每一个配置都写一个 RedisTemplate 配置

java 复制代码
    @Bean("oneRedisTemplate")
    public RedisTemplate<String, Object> oneRedisTemplate() {...}

    @Bean("twoRedisTemplate")
    public RedisTemplate<String, Object> twoRedisTemplate() {...}

3.使用方法可以与 properties 里面的配置直接对应,比如 oneRedisTemplate

java 复制代码
    @Autowired
    @Qualifier("oneRedisTemplate")
    private RedisTemplate<String, Object> redisTemplateOne;

对应 application.properties 中的 multi.redis.one 前缀

properties 复制代码
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

如果有时间,会在下篇文章中实现。

相关推荐
katasea27 分钟前
关于Springboot 应配置外移和Maven个性化打包一些做法
spring boot·maven
陈卓4101 小时前
Redis-限流方案
前端·redis·bootstrap
Huooya1 小时前
springboot的外部配置加载顺序
spring boot·面试·架构
钢板兽1 小时前
Java后端高频面经——Spring、SpringBoot、MyBatis
java·开发语言·spring boot·spring·面试·mybatis
用户53866168248222 小时前
SpringBoot 业务中 通过嵌套异步的方式 提高业务表数据同步效率
java·spring boot
MickeyCV2 小时前
《苍穹外卖》SpringBoot后端开发项目重点知识整理(DAY1 to DAY3)
java·spring boot·后端·苍穹外卖
morris1314 小时前
【redis】数据类型之geo
redis·地理位置·geo
学编程的小程4 小时前
Spring Boot应用开发:从零到生产级实战指南
spring boot
不修×蝙蝠4 小时前
SpringBoot(一)--搭建架构5种方法
java·spring boot·架构·配置·搭建
风象南5 小时前
Spring Boot 的 20个实用技巧
java·spring boot