文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

实用:Spring的多租户数据源管理 AbstractRoutingDataSource!

2024-12-02 09:38

关注

本文转载自微信公众号「小姐姐味道」,作者小姐姐养的狗02号  。转载本文请联系小姐姐味道公众号。

很多情况,我们确实需要在一个服务中访问多个数据源。虽然它让整体设计变的不那么优雅,但真实的世界确实需要它。比如,你的业务为两个比较大的客户服务,但你希望他们能够共用一套代码。

也就是说,你的代码刚开始没有考虑设计多租户这种功能,但后面又有这种蛋疼的需求。但还好不是爆炸式的租户增长。

除了引入一些分库分表组件,Spring自身提供了AbstractRoutingDataSource的方式,让多数数据源的管理成为可能。其实分库分表组件使用上限制很多,你不得不首先梳理这座屎山,接下来还要忍受中间件对你的SQL的苛刻要求;反而是一些野路子,能够让代码的改动量尽量的减少。

心动不如行动。接下来,就让我们来看一下它的具体实现吧。

1.基本原理

多数据源能进行动态切换的核心就是spring底层提供了AbstractRoutingDataSource类进行数据源路由。AbstractRoutingDataSource实现了DataSource接口,所以我们可以将其直接注入到DataSource的属性上。

我们主要继承这个类,实现里面的方法determineCurrentLookupKey(),而此方法只需要返回一个数据库的名称即可。

比如,Controller通过拿到前端业务传递的数值,进行业务逻辑分发。它就可以手动设置当前请求的数据库标识,然后路由到正确的库表里面。

  1. @Controller 
  2. public class ARDTestController { 
  3.     @GetMapping("test"
  4.     public void chifeng(){ 
  5.         //db-a 应该是上层传递下来的属性,我们可以把它放在ThreadLocal里 
  6.         DataSourceContextHolder.setDbKey("db-a"); 
  7.     } 

那么当sql语句执行的时候,它如何知道自己需要切换到哪个数据源呢?是不是需要把db-a这个属性一直透传下去呢?

在Java中,可以使用ThreadLocal绑定这个透传的属性。像Spring的嵌套事务等实现的原理,也是基于ThreadLocal去运行的。所以,DataSourceContextHolder.本质上是一个操作ThreadLocal的类。

  1. public class DataSourceContextHolder { 
  2.     private static InheritableThreadLocal dbKey = new InheritableThreadLocal<>(); 
  3.  
  4.     public static void setDbKey(String key){ 
  5.         dbKey.set(key); 
  6.     } 
  7.  
  8.     public static String getDbKey(){ 
  9.         return dbKey.get(); 
  10.     } 

2.配置代码

首先,我们自定义了配置文件的格式。如下面的代码,就配置了db-a和db-b两个数据库。

  1. multi: 
  2.   dbs: 
  3.     db-a: 
  4.       driver-class-name: org.h2.Driver 
  5.       url: jdbc:h2:mem:dba;MODE=MYSQL;DATABASE_TO_UPPER=false
  6.     db-b: 
  7.       driver-class-name: org.h2.Driver 
  8.       url: jdbc:h2:mem:dbb;MODE=MYSQL;DATABASE_TO_UPPER=false

然后,我们将它解析称properties。

  1. @ConfigurationProperties(prefix = "multi"
  2. @Configuration 
  3. public class DbsProperties { 
  4.     private Map> dbs = new HashMap<>(); 
  5.  
  6.     public Map> getDbs() { 
  7.         return dbs; 
  8.     } 
  9.  
  10.     public void setDbs(Map> dbs) { 
  11.         this.dbs = dbs; 
  12.     } 

接下来一步,需要配置整个应用所默认的数据源。如你所见,它的主要逻辑,就是在运行的时候,从ThreadLocal里取出提前设置的这个值。

  1. public class DynamicDataSource extends AbstractRoutingDataSource { 
  2.     @Override 
  3.     protected Object determineCurrentLookupKey() { 
  4.         return DataSourceContextHolder.getDbKey(); 
  5.     } 

最后一步,设置整个项目中默认的DataSource。注意,我们生成DynamicDataSource之后,还需要提供targetDataSource和defaultTargetDataSource两个属性的值,才能够正常运行。

  1. @Configuration 
  2. public class DynamicDataSourceConfiguration { 
  3.     @Autowired 
  4.     DbsProperties properties; 
  5.  
  6.     @Bean 
  7.     public DataSource dataSource(){ 
  8.         DynamicDataSource dataSource = new DynamicDataSource(); 
  9.         final Map targetDataSource  = getTargetDataSource(); 
  10.         dataSource.setTargetDataSources(targetDataSource); 
  11.         //TODO 默认数据库需要设置 
  12.         dataSource.setDefaultTargetDataSource(targetDataSource.values().iterator().next()); 
  13.         return dataSource; 
  14.     } 
  15.  
  16.     private Map getTargetDataSource(){ 
  17.         Map dataSources = new HashMap<>(); 
  18.         this.properties.getDbs().entrySet().stream() 
  19.                 .forEach(e->{ 
  20.                     DriverManagerDataSource dmd = new DriverManagerDataSource(); 
  21.                     dmd.setUrl(e.getValue().get("url")); 
  22.                     dmd.setDriverClassName(e.getValue().get("driver-class-name")); 
  23.                     dataSources.put(e.getKey(),dmd); 
  24.                 }); 
  25.         return  dataSources; 
  26.     } 

3.问题

通过以上简单的代码,就可以实现Spring简单的多数据源管理。但明显的,它还存在很多问题。

End

对于一个微服务来说,有很多默认的限制策略,比如,不同域之间的服务是不能共享一个数据库的。这些基本原则,把微服务整的清清爽爽,是一些基本的原则。

同理的,如果我们在设计开始,就给每一张表加上租户的字段ID,那么写代码的时候就顺畅的多。但是世界上没有这么多如果。

原则为何而存在?当然是为了让人去打破的。

编程只是工具,反正代码在自己手里,怎么玩,看需要,也看心情。条条大路通罗马,曲径通幽处,风光无限好。

 

作者简介:小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。

 

来源:小姐姐味道内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯