动态数据源切换实践

admin 7272

在项目中,有遇到随业务场景的不同,而切换不同库的操作,本来考虑利用mycat来实现,但后来发现基于mycat本身的适用场景与我们的需求并不符,如果用的话,会有两个问题:

mycat需要单独部署,虽不大,但又增加了现场的docker数,浪费资源;我们的业务并不是基于数据库字段来切库,并且mycat已提供的几种切库方式没有我们的场景,要套用的话,虽可用注解的方式来加, 但对代码侵入性有点高,而且需要对jpa 和 mybatic 进行分别组装sql ;故,最后采用自己写代码封装实现切库的方式来完成, 具体方法依赖:Spring中的AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。

具体实现切库步骤:

1.在platform-common工程中建以下三个类:DataSourceHolder、DynamicDataSource、SwitchDataSource

具体类的实现如下:

package com.star.boss.common.switchdatesource;

/**

* Created by 10000474 on 2018/6/8 0008.

*/

public class DataSourceHolder {

//使用ThreadLocal把数据源与当前线程绑定

private static final ThreadLocal dataSources = new ThreadLocal();

public static void setDataSource(String dataSourceName) {

dataSources.set(dataSourceName);

}

public static String getDataSource() {

return (String) dataSources.get();

}

public static void clearDataSource() {

dataSources.remove();

}

}

package com.star.boss.common.switchdatesource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

* Created by 10000474 on 2018/6/8 0008.

*/

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return DataSourceHolder.getDataSource();

}

}

package com.star.boss.common.switchdatesource;

import javax.servlet.*;

import java.io.IOException;

/**

* Created by 10000474 on 2018/6/8 0008.

*/

public class SwitchDataSource implements Filter {

@Override

public void init(FilterConfig filterConfig) throws ServletException {

}

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

//此处比较简单,根据自己的业务可扩展;

System.out.println("... start switch datasource ...");

DataSourceHolder.setDataSource("dataSource2"); //这是要切的dataSoure的名称;

System.out.println("... end to switch datasource ...");

System.out.println(DataSourceHolder.getDataSource());

chain.doFilter(request, response);

}

@Override

public void destroy() {

}

}

2. 在具体项目中的dao配置文件里做以下工作:

先配置两数据源:dataSource1、dataSource2

class="com.alibaba.druid.pool.DruidDataSource">

value="${maxOpenPreparedStatements}"/>

class="com.alibaba.druid.pool.DruidDataSource">

value="${maxOpenPreparedStatements}"/>

并定义动态数据源:dataSource

其它地方配置照旧

3.在具体项目中web.xml 中增加以下配置:

switchDataSourceFilter

com.star.boss.common.switchdatesource.SwitchDataSource

switchDataSourceFilter

/*

Ok,测试完成;还有一点需要注意的是,一定要在注入bean之前,就要切库,否则一切白费,由于我们项目中前后端分离,又用的注解注入,我开始的时候,想在service层切库,一切貌似正常,实际已经是马后炮了,大家不要犯这样的错误;

另外, 由于一个服务如果要切源,会引起需要初始化多个库的需求,这个需要在flyway中实现,注意一下配置参数又交给zookeep;(请将MamjTest改为有业务意义的名字!)

扩展:

切库的代码,此处因为只是调研版,所以没做优化,真正在项目中,建议用注解的方式来实现,不要手动切,显得比较笨 。

另外,如果分源存数据,会引进事务问题,由于我们项目中,只是查询的时候要切库,存库时已保证了只针对一个库,所以不存在此问题,所以没做研究。