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

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

相关推荐
代码之光_198018 分钟前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
minihuabei1 小时前
linux centos 安装redis
linux·redis·centos
戴眼镜的猴1 小时前
Spring Boot的过滤器与拦截器的区别
spring boot
尘浮生2 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
尚学教辅学习资料2 小时前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理
morris1313 小时前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
monkey_meng3 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
hlsd#4 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
奶糖趣多多6 小时前
Redis知识点
数据库·redis·缓存
阿伟*rui6 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel