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

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

相关推荐
有你有我OK20 小时前
springboot Admin 服务端 客户端配置
spring boot·后端·elasticsearch
xiaoopin1 天前
简单的分布式锁 SpringBoot Redisson‌
spring boot·分布式·后端
青鱼入云1 天前
redisson介绍
redis·1024程序员节
哲Zheᗜe༘1 天前
了解学习Redis主从复制
数据库·redis·学习
一条懒鱼6661 天前
Redis Sentinel哨兵集群
数据库·redis·sentinel
Zhu_S W1 天前
Redis跳表:高效有序数据结构的深度剖析
数据结构·数据库·redis
霸道流氓气质1 天前
SpringBoot+MybatisPlus+自定义注解+切面实现水平数据隔离功能(附代码下载)
java·spring boot·后端
韩立学长1 天前
【开题答辩实录分享】以《智慧校园勤工俭学信息管理系统的设计与实现》为例进行答辩实录分享
vue.js·spring boot·微信小程序
克莱恩~莫雷蒂1 天前
Spring Boot 中 controller层注解
java·spring boot·后端
初学者_xuan1 天前
零基础新手小白快速了解掌握服务集群与自动化运维(十五)Redis模块-哨兵集群
运维·redis·自动化