使用DataSource Switch很简单,在pom.xml配置文件内添加如下依赖:

    ApiBoot所提供的依赖都不需要添加版本号,具体查看

    集成数据源实现

    目前ApiBoot DataSource Switch集成了DruidHikariCP两种数据源实现依赖,在使用方面也有一定的差异,因为每一个数据源的内置参数不一致。

    • Druid:参数配置前缀为api.boot.datasource.druid
    • HikariCP:参数配置前缀为api.boot.datasource.hikari 具体使用请查看下面功能配置介绍。

    配置参数

    HikariCP数据源是SpringBoot2.x自带的,配置参数请访问HikariCP

    ApiBoot DataSource Switch支持单主数据源的配置,application.yml配置文件如下所示:

    1. api:
    2. boot:
    3. datasource:
    4. # 配置使用hikari数据源
    5. hikari:
    6. # master datasource config
    7. master:
    8. url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
    9. username: root
    10. password: 123456

    修改主数据源名称

    master为默认的主数据源的poolName,这里可以进行修改为其他值,不过需要对应修改primary参数,如下所示:

    1. api:
    2. boot:
    3. datasource:
    4. # 主数据源,默认值为master
    5. primary: main
    6. # 配置使用hikari数据源
    7. hikari:
    8. # main datasource config
    9. main:
    10. url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
    11. username: root
    12. password: 123456

    在上面配置主数据源的poolName修改为main

    主从配置

    如果你的项目内存在单主单从一主多从的配置方式,如下所示:

    1. api:
    2. boot:
    3. datasource:
    4. # 配置使用hikari数据源
    5. hikari:
    6. # master datasource config
    7. master:
    8. url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
    9. username: root
    10. password: 123456
    11. # 默认值为【com.mysql.cj.jdbc.Driver】
    12. #driver-class-name: com.mysql.cj.jdbc.Driver
    13. # slave 1 datasource config
    14. slave_1:
    15. username: root
    16. password: 123456
    17. # slave 2 datasource config
    18. slave_2:
    19. url: jdbc:mysql://localhost:3306/resources?characterEncoding=utf8&serverTimezone=Asia/Shanghai
    20. username: root
    21. password: 123456

    在上面是一主多从的配置方式,分别是masterslave_1slave_2

    ApiBoot DataSource Switch提供了一个项目内连接多个不同类型的数据库,如:MySQLOracle…等,如下所示:

    在上面配置中,master主数据源使用的MySQL驱动连接MySQL数据库,而slave从数据源则是使用的Oracle驱动连接的Oracle数据库。

    动态创建数据源

    ApiBoot DataSource Switch内部提供了动态创建数据源的方法,可以通过注入ApiBootDataSourceFactoryBean来进行添加,如下所示:

    1. @Autowired
    2. private ApiBootDataSourceFactoryBean factoryBean;

    3. public void createNewDataSource() throws Exception {

    4. // 如果创建Druid数据源,使用DataSourceDruidConfig

    5. DataSourceHikariConfig config = new DataSourceHikariConfig();

    6. // 数据库连接:必填

    7. config.setUrl("jdbc:mysql://localhost:3306/resources");

    8. // 用户名:必填

    9. config.setUsername("root");

    10. // 密码:必填

    11. config.setPassword("123456");

    12. // 数据源名称:必填(用于@DataSourceSwitch注解value值使用)

    13. config.setPoolName("dynamic");

    14. // 创建数据源

    15. DataSource dataSource = factoryBean.newDataSource(config);

    16. Connection connection = dataSource.getConnection();

    17. System.out.println(connection.getCatalog());

    18. connection.close();

    自动切换

    ApiBoot DataSource Switch的数据源自动切换主要归功于的AOP,通过切面@DataSourceSwitch注解,获取注解配置的value值进行设置当前线程所用的数据源名称,从而通过AbstractRoutingDataSource进行数据源的路由切换。

    我们沿用上面一主多从的配置进行代码演示,配置文件application.yml参考上面配置,代码示例如下:

    从数据源示例类

    1. @Service
    2. @DataSourceSwitch("slave")
    3. public class SlaveDataSourceSampleService {
    4. /**
    5. * DataSource Instance
    6. */
    7. @Autowired
    8. private DataSource dataSource;
    9.  
    10. /**
    11. * 演示输出数据源的catalog
    12. *
    13. * @throws Exception
    14. */
    15. public void print() throws Exception {
    16. // 获取链接
    17. Connection connection = dataSource.getConnection();
    18. // 输出catalog
    19. System.out.println(this.getClass().getSimpleName() + " ->" + connection.getCatalog());
    20. // 关闭链接
    21. connection.close();
    22. }
    23. }

    主数据源示例类

    1. @Service
    2. @DataSourceSwitch("master")
    3. /**
    4. * DataSource Instance
    5. */
    6. @Autowired
    7. private DataSource dataSource;
    8. /**
    9. * Slave Sample Service
    10. */
    11. @Autowired
    12. private SlaveDataSourceSampleService slaveDataSourceSampleService;
    13.  
    14. /**
    15. * 演示输出主数据源catalog
    16. * 调用从数据源类演示输出catalog
    17. *
    18. * @throws Exception
    19. */
    20. public void print() throws Exception {
    21. Connection connection = dataSource.getConnection();
    22. System.out.println(this.getClass().getSimpleName() + " ->" + connection.getCatalog());
    23. connection.close();
    24. slaveDataSourceSampleService.print();
    25. }
    26. }
    • 主数据源的示例类内,我们通过@DataSourceSwitch("master")注解的value进行定位连接master数据源数据库。
    • 同样在从数据库的示例类内,我们也可以通过@DataSourceSwitch("slave")注解的value进行定位连接slave数据源数据库。

    单元测试示例

    在上面的测试示例中,我们使用交叉的方式进行验证数据源路由是否可以正确的进行切换,可以编写一个单元测试进行验证结果,如下所示:

    运行上面测试方法,结果如下所示:

    1. MasterDataSourceSampleService ->test
    2. 2019-04-04 10:20:45.407 INFO 7295 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
    3. 2019-04-04 10:20:45.411 INFO 7295 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
    4. SlaveDataSourceSampleService ->oauth2

    单次执行数据源切换没有任何的问题,master数据源获取catalog输出后,调用slave示例类进行输出catalog

    单次执行单线程操作没有问题,不代表多线程下不会出现问题,在开头说到过ApiBoot DataSource Switch是线程安全的,所以接下来我们来验证这一点,我们需要添加压力测试的依赖,如下所示:

    1. <dependency>
    2. <groupId>org.databene</groupId>
    3. <artifactId>contiperf</artifactId>
    4. <version>2.3.4</version>
    5. <scope>test</scope>
    6. </dependency>

    接下来把上面的单元测试代码改造下,如下所示:

    1. // 初始化压力性能测试对象
    2. @Rule
    3. public ContiPerfRule i = new ContiPerfRule();
    4.  
    5. @Autowired
    6. private MasterDataSourceSampleService masterDataSourceSampleService;
    7. /**
    8. * 开启500个线程执行10000次
    9. */
    10. @Test
    11. @PerfTest(invocations = 10000, threads = 500)
    12. public void contextLoads() throws Exception {
    13. masterDataSourceSampleService.print();
    14. }

    测试环境:

    硬件:i7、16G、256SSD

    系统:OS X

    整个过程大约是10秒左右,ApiBoot DataSource Switch并没有发生出现切换错乱的情况。

    注意事项

    • 在使用ApiBoot DataSource Switch时需要添加对应数据库的依赖
    • 如果使用Druid连接池,不要配置使用druid-starter的依赖,请使用druid依赖。