首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Springboot 整合redis 多数据源 数据库切换

Springboot 整合redis 多数据源 数据库切换

作者头像
分享干货的你
发布于 2022-12-19 12:40:41
发布于 2022-12-19 12:40:41
2.4K01
代码可运行
举报
文章被收录于专栏:分享干货的你分享干货的你
运行总次数:1
代码可运行

在日常的开发过程中我们都使用过redis {nosql} 做缓存什么的。 基本上都是使用官方的data-redis 来进行整合使用。 但是官方的只能支持单数据源的, 不支持多数据源的。 要是配置多数据源的情况下, 还要配置多个redisConnectionfactory , 配置多个redistemplate 同样的代码要写多份。这个很不友好,最近在想,能不能搞一个starts 封装一下。类似mybatis-plus 团队的动态数据源一样是基于注解和配置文件的。 我在网上找了很多资料,大部分都是怎么切换redis 数据库的, 没有切换redis数据源的。最后在知乎上面找到老哥的这篇文章, https://zhuanlan.zhihu.com/p/405242915 (如有侵权,请联系删除)。给了我新思路的大门。下面我们就来自己搞一个基于配置文件和注解的redis 动态数据源和动态数据库的切换。

1, 准备工作,新建一个一个springboot maven 工程, 这里我们不需要web的依赖,只需要data-redis 的依赖就行的。

2, 代码逻辑

3, 正式的写代码

大部分的代码都和之前那个老哥文章代码差不多, 这里我只是加上了切换redis 数据库的逻辑。

核心代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.ducheng.multi.redis;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.util.ObjectUtils;
import java.util.Map;

public class MultiRedisConnectionFactory
        implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
    private final Map<String, LettuceConnectionFactory> connectionFactoryMap;

    /**
     *
     * 当前redis的名字
     *
     */
    private static final ThreadLocal<String> currentRedisName = new ThreadLocal<>();

    /**
     *  当前redis的db数据库
     */
    private static final ThreadLocal<Integer> currentRedisDb = new ThreadLocal<>();


    public MultiRedisConnectionFactory(Map<String, LettuceConnectionFactory> connectionFactoryMap) {
        this.connectionFactoryMap = connectionFactoryMap;
    }

    public void setCurrentRedis(String currentRedisName) {
        if (!connectionFactoryMap.containsKey(currentRedisName)) {
            throw new RuntimeException("invalid currentRedis: " + currentRedisName + ", it does not exists in configuration");
        }
        MultiRedisConnectionFactory.currentRedisName.set(currentRedisName);
    }

    /**
     * 选择连接和数据库
     * @param currentRedisName
     * @param db
     */
    public void setCurrentRedis(String currentRedisName,Integer db) {
        if (!connectionFactoryMap.containsKey(currentRedisName)) {
            throw new RuntimeException("invalid currentRedis: " + currentRedisName + ", it does not exists in configuration");
        }
        MultiRedisConnectionFactory.currentRedisName.set(currentRedisName);
        MultiRedisConnectionFactory.currentRedisDb.set(db);
    }


    @Override
    public void destroy() throws Exception {
        connectionFactoryMap.values().forEach(LettuceConnectionFactory::destroy);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        connectionFactoryMap.values().forEach(LettuceConnectionFactory::afterPropertiesSet);
    }

    private LettuceConnectionFactory currentLettuceConnectionFactory() {
        String currentRedisName = MultiRedisConnectionFactory.currentRedisName.get();
        if (!ObjectUtils.isEmpty(currentRedisName)) {
            MultiRedisConnectionFactory.currentRedisName.remove();
            return connectionFactoryMap.get(currentRedisName);
        }
        return connectionFactoryMap.get(MultiRedisProperties.DEFAULT);
    }

    @Override
    public ReactiveRedisConnection getReactiveConnection() {
        return currentLettuceConnectionFactory().getReactiveConnection();
    }

    @Override
    public ReactiveRedisClusterConnection getReactiveClusterConnection() {
        return currentLettuceConnectionFactory().getReactiveClusterConnection();
    }

    @Override
    public RedisConnection getConnection() {
    // 这里就是切换数据库的地方
        Integer currentRedisDb = MultiRedisConnectionFactory.currentRedisDb.get();
        if (!ObjectUtils.isEmpty(currentRedisDb)) {
            LettuceConnectionFactory lettuceConnectionFactory = currentLettuceConnectionFactory();
            lettuceConnectionFactory.setShareNativeConnection(false);
            RedisConnection connection = lettuceConnectionFactory.getConnection();
            connection.select(currentRedisDb);
            return connection;
        }
        return   currentLettuceConnectionFactory().getConnection();
    }

    @Override
    public RedisClusterConnection getClusterConnection() {
        return currentLettuceConnectionFactory().getClusterConnection();
    }

    @Override
    public boolean getConvertPipelineAndTxResults() {
        return currentLettuceConnectionFactory().getConvertPipelineAndTxResults();
    }

    @Override
    public RedisSentinelConnection getSentinelConnection() {
        return currentLettuceConnectionFactory().getSentinelConnection();
    }

    @Override
    public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
        return currentLettuceConnectionFactory().translateExceptionIfPossible(ex);
    }

}

根据条件注解注入redis 数据库的工厂

核心代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.springframework.boot.autoconfigure.data.redis;


import com.ducheng.multi.redis.MultiRedisConnectionFactory;
import com.ducheng.multi.redis.MultiRedisProperties;
import io.lettuce.core.resource.ClientResources;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

import java.util.HashMap;
import java.util.Map;

@ConditionalOnProperty(prefix = "spring.redis", value = "enable-multi", matchIfMissing = false)
@Configuration(proxyBeanMethods = false)
public class RedisCustomizedConfiguration {

    /**
     * @param builderCustomizers
     * @param clientResources
     * @param multiRedisProperties
     * @return
     * @see org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration
     */
    @Bean
    public MultiRedisConnectionFactory multiRedisConnectionFactory(
            ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
            ClientResources clientResources,
            MultiRedisProperties multiRedisProperties,
            ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
            ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
        Map<String, LettuceConnectionFactory> connectionFactoryMap = new HashMap<>();
        Map<String, RedisProperties> multi = multiRedisProperties.getMulti();
        multi.forEach((k, v) -> {
            LettuceConnectionConfiguration lettuceConnectionConfiguration = new LettuceConnectionConfiguration(
                    v,
                    sentinelConfigurationProvider,
                    clusterConfigurationProvider
            );
            LettuceConnectionFactory lettuceConnectionFactory = lettuceConnectionConfiguration.redisConnectionFactory(builderCustomizers, clientResources);
            connectionFactoryMap.put(k, lettuceConnectionFactory);
        });
        return new MultiRedisConnectionFactory(connectionFactoryMap);
    }

}

redis 的配置类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.ducheng.multi.redis;


import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Map;


@ConfigurationProperties(prefix = "spring.redis")
public class MultiRedisProperties {
    /**
     * 默认连接必须配置,配置 key 为 default
     */
    public static final String DEFAULT = "default";

    private boolean enableMulti = false;


    private Map<String, RedisProperties> multi;

    public boolean isEnableMulti() {
        return enableMulti;
    }

    public void setEnableMulti(boolean enableMulti) {
        this.enableMulti = enableMulti;
    }

    public Map<String, RedisProperties> getMulti() {
        return multi;
    }

    public void setMulti(Map<String, RedisProperties> multi) {
        this.multi = multi;
    }

    public MultiRedisProperties() {
    }
}

代码测试:

配置文件配置多数据源:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spring.redis.enable-multi=true
spring.redis.multi.default.host=xxxxxxxx
spring.redis.multi.default.port=6381
spring.redis.multi.test.host=xxxxxxxx
spring.redis.multi.test.port=6380

配置redisTemplate

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Bean
  public RedisTemplate<String, Object> template(RedisConnectionFactory factory) {
    // 创建RedisTemplate<String, Object>对象
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    // 配置连接工厂
    template.setConnectionFactory(factory);
    // 定义Jackson2JsonRedisSerializer序列化对象
    Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
    ObjectMapper om = new ObjectMapper();
    // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jacksonSeial.setObjectMapper(om);
    StringRedisSerializer stringSerial = new StringRedisSerializer();
    // redis key 序列化方式使用stringSerial
    template.setKeySerializer(stringSerial);
    // redis value 序列化方式使用jackson
    template.setValueSerializer(jacksonSeial);
    // redis hash key 序列化方式使用stringSerial
    template.setHashKeySerializer(stringSerial);
    // redis hash value 序列化方式使用jackson
    template.setHashValueSerializer(jacksonSeial);
    template.afterPropertiesSet();
    return template;
  }

编写测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.ducheng.multi.redis;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class MultiRedisSourceApplicationTests {

  @Autowired
  RedisTemplate<String, Object> redisTemplate;

  @Autowired
  MultiRedisConnectionFactory multiRedisConnectionFactory;

  @Test
  void contextLoads() {
    // 走默认的数据源
   redisTemplate.opsForValue().set("k1","v1");
    // 走test数据源0 库
   multiRedisConnectionFactory.setCurrentRedis("test",0);
   redisTemplate.opsForValue().set("k1","v2");
    // 走test数据源9 库
   multiRedisConnectionFactory.setCurrentRedis("test",9);
   redisTemplate.opsForValue().set("k1","v2");
  }

}

最后结果

完美,自定义注解加上aop 来动态切换,就是定义一个自定义的注解里面包含库名称和db 的名称

然后 就是在aop 的前置拦截器上面,或者注解的值, 然后在用MultiRedisConnectionFactory 来设置数据源和db 。相关的代码我就不写了,大家可以自己实现。后面我会封装一下,上传maven仓库。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 分享干货的你 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
springboot集成了哪些框架_redis java客户端
使用注解进行缓存操作涉及CacheManage RedisCacheManager源码
全栈程序员站长
2022/11/08
1.2K0
Springboot2.x集成lettuce连接redis集群报超时异常Command timed out after 6 second(s)
背景:最近在对一新开发Springboot系统做压测,发现刚开始压测时,可以正常对redis集群进行数据存取,但是暂停几分钟后,接着继续用jmeter进行压测时,发现redis就开始突然疯狂爆出异常提示:Command timed out after 6 second(s)......
朱季谦
2021/03/26
4.1K0
Springboot2.x集成lettuce连接redis集群报超时异常Command timed out after 6 second(s)
SpringBoot2.x整合Redis数据库
1、Redis是当下最流行的用于实现缓存机制的NoSQL数据库,其主要通过key-value存储,支持高并发访问。在实际工作中,Redis结合SpringData技术后可以方便地实现序列化对象的存储。SpringBoot很好地支持了Redis,可以在项目中使用SpringData进行Redis数据操作。
别先生
2020/11/26
2.7K0
SpringBoot2.x整合Redis数据库
当SpringBoot引用Redis依赖但没有配置Redis信息时
虽然引用了这个公共模块,但是没有配置相对应的**Redis**信息,在项目启动后会出现异常报错
半月无霜
2024/05/17
7260
当SpringBoot引用Redis依赖但没有配置Redis信息时
Spring boot配置多个Redis数据源操作实例
平时都是使用本地环境的单Redis情况比较多,在集群环境的情况下连接多个Redis数据库是很正常的情况。
程序员小强
2019/06/02
13.2K0
redis读写分离之lettuce
redis使用过程中,很多情况都是读多写少,而不管是主从、哨兵、集群,从节点都只是用来备份,为了最大化节约用户成本,我们需要利用从节点来进行读,分担主节点压力,这里我们继续上一章的jedis的读写分离,由于springboot现在redis集群默认用的是lettuce,所以介绍下lettuce读写分离
一笠风雨任生平
2022/01/06
3K0
redis读写分离之lettuce
Springboot 2.0 ——集成redis
最近在入门SpringBoot,然后在感慨 SpringBoot较于Spring真的方便多时,顺便记录下自己在集成redis时的一些想法。
烂猪皮
2018/10/18
7930
SpringBoot一键集成Redis
您好,我是码农飞哥,感谢您阅读本文!本文主要介绍SpringBoot如何整合Redis。
码农飞哥
2021/08/18
4220
spring-data-redis中lettuce pipeline的坑之解决篇
我们先来了解下在spring-data-redis中是如何包装lettuce的连接的,然后会根据这些信息得到上一篇文章中留下的那个问题的解。
山行AI
2020/07/14
7.1K4
spring-data-redis中lettuce pipeline的坑之解决篇
SpringBoot 整合 Redis
因为 Redis 有 这5种基本数据结构类型分别为:String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。
收心
2022/01/17
4110
SpringBoot 整合 Redis
SpringBoot 2.x Redis缓存乱码问题/自定义SpringBoot-Cache序列化方式
代码 @Cacheable(cacheNames = "article", cacheManager = "cacheManager", keyGenerator = "keyGenerator", condition = "#id!=null && #id!=''", unless = "#id==1") @Override public Article byId(String id)
喜欢天文的pony站长
2020/06/29
1.2K0
SpringBoot 2.x Redis缓存乱码问题/自定义SpringBoot-Cache序列化方式
自定义SpringCache For Redis配置
SpringCache抽象了缓存使用场景, 对外提供注解, 无需繁琐配置即可获得缓存能力
code-x
2022/06/28
5430
带Lettuce连接池、多数据源配置的RedisTemplate方案
在现实项目中,我们往往会遇到需要使用多个Redis数据源的场景。本文介绍的是一种高度定制化的方案。每个独立的数据源都会使用自己的配置,其中包括针对该数据源的连接池配置。 要完成这个功能,需要解决几个基础能力:
方亮
2024/05/24
7200
Redis配置多数据源
Redis具有多个databases,通过CONFIG GET databases命令可查看,Redis不同的database间具有数据隔离性,即在项目中我们配置一个databases 0信息后,并无法获取其他databases的数据,所以,当我们业务中需要获取其他databases时,便需要配置多Redis数据源。
关忆北.
2022/05/05
7270
Redis配置多数据源
SpringBoot之redis
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
王念博客
2019/07/25
3640
Redis - Spring Data Redis 操作 Jedis 、Lettuce 、 Redisson
https://spring.io/projects/spring-data-redis
小小工匠
2021/08/17
2.7K0
Redis---整合SpringBoot篇
Redis6 1、引入redis相关依赖 2、application.properties配置redis配置 3.添加redis配置类 4.测试一下 RedisTemplate基本使用如下 ---- 1、引入redis相关依赖 <!-- redis的启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </depende
大忽悠爱学习
2021/11/15
3210
0x02 SpringBoot使用注解方式同时集成Redis、Ehcache
链接:https://pan.baidu.com/s/1xZqpQQOcVgkr0W_FrItHRw 密码:x2jo
上善若水.夏
2018/09/28
2.8K0
SpringBoot集成Redis,并自定义对象序列化
SpringBoot项目使用redis非常简单,pom里面引入redis的场景启动器,在启动类上加@EnableCaching注解,项目启动会自动匹配上redis,这样项目中就可以愉快地使用了, 使用方法:要么使用@Cacheable一类的注解自动缓存,要么使用RedisTemplate手动缓存。 (前提是你的本机或者是远程主机要先搭好redis环境) 虽然SpringBoot好用,但这里也有好多坑,SpringBoot和MySQL一样,易学难精,阳哥说的对,练武不练功,到老一场空。 下面,我将详细记录整合步骤
全栈程序员站长
2022/11/08
8480
SpringBoot集成Redis,并自定义对象序列化
聊聊Spring AI的RedisVectorStore
org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java
code4it
2025/04/06
2330
聊聊Spring AI的RedisVectorStore
推荐阅读
相关推荐
springboot集成了哪些框架_redis java客户端
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验