本文转载自微信公众号「密斯姐滋味」,作家密斯姐养的狗02号 。转载本文请关系密斯姐滋味公众号。
很厚情况,咱们如实需要在一个干事中拜访多个数据源。诚然它让合座盘算变的不那么优雅,但信得过的全国如实需要它。比如,你的业务为两个比拟大的客户干事,但你但愿他们大约共用一套代码。
也即是说,你的代码刚运转莫得议论盘算多佃户这种功能,但后头又有这种蛋疼的需求。但还好不是爆炸式的佃户增长。
除了引入一些分库分表组件,Spring自己提供了AbstractRoutingDataSource的神色,让巨额数据源的解决成为可能。其实分库分表组件使用上汗漫许多,你不得不最初梳理这座屎山,接下来还要哑忍中间件对你的SQL的暴虐条目;反而是一些野门路,大约让代码的蜕变量尽量的减少。
心动不如当作。接下来,就让咱们来看一下它的具体达成吧。
1.基得意趣巨额据源能进当作态切换的中枢即是spring底层提供了AbstractRoutingDataSource类进行数据源路由。AbstractRoutingDataSource达成了DataSource接口,是以咱们不错将其径直注入到DataSource的属性上。
咱们主要秉承这个类,达成内部的规范determineCurrentLookupKey(),而此规范只需要复返一个数据库的称号即可。
比如,Controller通过拿到前端业务传递的数值,进行业务逻辑分发。它就不错手动建筑面前肯求的数据库记号,然后路由到正确的库表内部。
@Controller public class ARDTestController { @GetMapping("test") public void chifeng(){ //db-a 应该是表层传递下来的属性,咱们不错把它放在ThreadLocal里 DataSourceContextHolder.setDbKey("db-a"); } }
那么当sql语句扩张的工夫,它如何清亮我方需要切换到哪个数据源呢?是不是需要把db-a这个属性一直透传下去呢?
在Java中,不错使用ThreadLocal绑定这个透传的属性。像Spring的嵌套事务等达成的旨趣,亦然基于ThreadLocal去运行的。是以,DataSourceContextHolder.本色上是一个操作ThreadLocal的类。
public class DataSourceContextHolder { private static InheritableThreadLocal<String> dbKey = new InheritableThreadLocal<>(); public static void setDbKey(String key){ dbKey.set(key); } public static String getDbKey(){ return dbKey.get(); } }
2.设置代码
最初,咱们自界说了设置文献的风物。如底下的代码,就设置了db-a和db-b两个数据库。
multi: dbs: db-a: driver-class-name: org.h2.Driver url: jdbc:h2:mem:dba;MODE=MYSQL;DATABASE_TO_UPPER=false; db-b: driver-class-name: org.h2.Driver url: jdbc:h2:mem:dbb;MODE=MYSQL;DATABASE_TO_UPPER=false;
然后,咱们将它办法称properties。
@ConfigurationProperties(prefix = "multi") @Configuration public class DbsProperties { private Map<String, Map<String, String>> dbs = new HashMap<>(); public Map<String, Map<String, String>> getDbs() { return dbs; } public void setDbs(Map<String, Map<String, String>> dbs) { this.dbs = dbs; } }
接下来一步,需要设置总计这个词垄断所默许的数据源。如你所见,它的主要逻辑,即是在运行的工夫,从ThreadLocal里取出提前建筑的这个值。
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDbKey(); } }
终末一步,建筑总计这个词技俩中默许的DataSource。留意,咱们生成DynamicDataSource之后,还需要提供targetDataSource和defaultTargetDataSource两个属性的值,材干够平常运行。
@Configuration public class DynamicDataSourceConfiguration { @Autowired DbsProperties properties; @Bean public DataSource dataSource(){ DynamicDataSource dataSource = new DynamicDataSource(); final Map<Object,Object> targetDataSource = getTargetDataSource(); dataSource.setTargetDataSources(targetDataSource); //TODO 默许数据库需要建筑 dataSource.setDefaultTargetDataSource(targetDataSource.values().iterator().next()); return dataSource; } private Map<Object,Object> getTargetDataSource(){ Map<Object,Object> dataSources = new HashMap<>(); this.properties.getDbs().entrySet().stream() .forEach(e->{ DriverManagerDataSource dmd = new DriverManagerDataSource(); dmd.setUrl(e.getValue().get("url")); dmd.setDriverClassName(e.getValue().get("driver-class-name")); dataSources.put(e.getKey(),dmd); }); return dataSources; } }3.问题
通过以上粗浅的代码,就不错达成Spring粗浅的巨额据源解决。但彰着的,它还存在许多问题。
需要家具盘算选择风物,进行业务切换。 前端不错接纳放在localStroage的神色,保存属性,可使用阻碍器神色将变量每次都传递。 后端每次肯求,都需要带上主张db,不错接纳放在ThreadLocal里的神色。但ThreadLocal有线程透传的问题,若是任务里开启了子线程,则变量不成分享。 由于表是动态选择的,是以JPA自动创建和update等风物,将不可用。不便捷测试和单位测试,在测试接口的工夫,也需要每次强制指定指向的库。 由于是修改数据源的风物,每次加多库,都需要再行启动上线才不错。若是要做到动态性,数据源殉国事个问题。 End关于一个微干事来说,有许多默许的汗漫计谋,比如,不同域之间的干事是不成分享一个数据库的。这些基本原则,把微干事整的明昭着白,是一些基本的原则。
同理的,若是咱们在盘算运转,就给每一张表加上佃户的字段ID,那么写代码的工夫就顺畅的多。然而全国上莫得这样多若是。
原则为何而存在?天然是为了让人去碎裂的。
编程仅仅器具,归正代码在我方手里,若何玩,看需要,也看神志。条条正途通罗马,曲径通幽处,征象无穷好。
作家简介:密斯姐滋味 (xjjdog),一个不允许法子员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发全国,给你不通常的滋味。