sharding-jdbc 兼容 MybatisPlus的动态数据源

打印 上一主题 下一主题

主题 554|帖子 554|积分 1662

配景:之前的项目做读写分离的时候用的 MybatisPlus的动态数据做的,很多地方使用的@DS直接指定的读库大概写库实现的业务;随着表数据量越来越大,现在计划把比较大的表举行程度拆分,准备使用 ShardingJDBC实现,但是发现两者配合起来并不是那么顺利,网上大部分文章都是直接把整个Sharding的数据源当成MybatisPlus的一个数据源,那么在原本@DS上面指定的数据源就无法直接使用Sharding的分库等逻辑,以是我研究了一下源码,实现了这一逻辑,给后面有必要的朋侪提供一个案例,避免浪费不必要的时间
一.  版本选择

现在ShardingJDBC重要有两个版本,一个是ShardingJDBC早期版本,一个是ShardingSphere项目中的ShardingSphere-JDBC

  • Sharding-JDBC:Sharding-JDBC 最初由当时的项目发起人在2016年发布。它最早作为一个轻量级的 JDBC 层解决方案,旨在解决数据库分片和读写分离的问题。
  • ShardingSphere:ShardingSphere 项目是由 Sharding-JDBC 项目发展而来的,并在2018年正式发布。Apache ShardingSphere 致力于构建更为完整的分布式数据库管理生态系统,包含了 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar等多个组件。
现在独立的ShardingJDBC已经停更,使用到的最多的版本是 4.1.1
  1. <dependency>
  2.     <groupId>org.apache.shardingsphere</groupId>
  3.     <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
  4.     <version>4.1.1</version>
  5. </dependency>
复制代码
ShardingSphere项目现在不停处于更新迭代中,ShardingSphere-JDBC 是通过ShardingJDBC 更新迭代过来的,在原有代码的基础举行了一些优化和新功能加入,对于开发者而言,重要是参数的设置发生了一些调整。但是参数的作用和设置方式和以前一样;
这里我为了方便以后会使用到新特性,我直接使用的是 ShardingSphere-JDBC 5.2.1
官方帮助文档https://www.bookstack.cn/read/shardingsphere-5.1.0-zh/ecf18b21ab3f559c.md
  1. <dependency>
  2.         <groupId>org.apache.shardingsphere</groupId>
  3.         <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  4.         <version>5.2.1</version>
  5. </dependency>
复制代码
二. 项目依赖

案例全部的 Maven依赖如下:
  1.     <dependencies>
  2.         <dependency>
  3.             <groupId>org.springframework.boot</groupId>
  4.             <artifactId>spring-boot-starter-web</artifactId>
  5.         </dependency>
  6.         <dependency>
  7.             <groupId>org.projectlombok</groupId>
  8.             <artifactId>lombok</artifactId>
  9.         </dependency>
  10.         <dependency>
  11.             <groupId>com.zaxxer</groupId>
  12.             <artifactId>HikariCP</artifactId>
  13.             <version>3.4.5</version>
  14.         </dependency>
  15.         <dependency>
  16.             <groupId>com.baomidou</groupId>
  17.             <artifactId>mybatis-plus-boot-starter</artifactId>
  18.             <version>3.4.0</version>
  19.         </dependency>
  20.         
  21.         <dependency>
  22.             <groupId>com.baomidou</groupId>
  23.             <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  24.             <version>3.3.2</version>
  25.         </dependency>
  26.         <dependency>
  27.             <groupId>mysql</groupId>
  28.             <artifactId>mysql-connector-java</artifactId>
  29.             <version>8.0.33</version>
  30.         </dependency>
  31.         
  32.         <dependency>
  33.             <groupId>org.apache.shardingsphere</groupId>
  34.             <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  35.             <version>5.2.1</version>
  36.             <exclusions>
  37.                 <exclusion>
  38.                     <groupId>org.yaml</groupId>
  39.                     <artifactId>snakeyaml</artifactId>
  40.                 </exclusion>
  41.             </exclusions>
  42.         </dependency>
  43.         
  44.         <dependency>
  45.             <groupId>org.yaml</groupId>
  46.             <artifactId>snakeyaml</artifactId>
  47.             <version>1.33</version>
  48.         </dependency>
  49.         <dependency>
  50.             <groupId>org.springframework.boot</groupId>
  51.             <artifactId>spring-boot-starter-test</artifactId>
  52.             <scope>test</scope>
  53.         </dependency>
  54.     </dependencies>
复制代码
三. 参数设置

application.yml 设置
  1. server:
  2.   port: 8080
  3. mybatis-plus:
  4.   mapper-locations: classpath*:mybatis/*.xml
  5.   type-aliases-package: com.game.sharding.dto
  6.   configuration:
  7.     map-underscore-to-camel-case: false
  8.     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  9. spring:
  10.   application:
  11.     name: sharding-jdbc-test
  12.   sharding-sphere:
  13.     datasource:
  14.       names: master,write,read,read2
  15.       master:
  16.         type: com.zaxxer.hikari.HikariDataSource
  17.         driver-class-name: com.mysql.cj.jdbc.Driver
  18.         jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
  19.         username: root
  20.         password: 123456
  21.       write:
  22.         type: com.zaxxer.hikari.HikariDataSource
  23.         driver-class-name: com.mysql.cj.jdbc.Driver
  24.         jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
  25.         username: root
  26.         password: 123456
  27.       read:
  28.         type: com.zaxxer.hikari.HikariDataSource
  29.         driver-class-name: com.mysql.cj.jdbc.Driver
  30.         jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
  31.         username: root
  32.         password: 123456
  33.       read2:
  34.         type: com.zaxxer.hikari.HikariDataSource
  35.         driver-class-name: com.mysql.cj.jdbc.Driver
  36.         jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
  37.         username: root
  38.         password: 123456
  39.     rules:
  40.       sharding:
  41.         tables:
  42.           team_msg:
  43.                   ## 这里的customer-ds是下面配置的读写分离的数据源名称
  44.             actual-data-nodes: customer-ds.team_msg_${0..1}
  45.             table-strategy:
  46.               standard:
  47.                 sharding-column: id
  48.                 sharding-algorithm-name: msg-id # 对应下面的sharding-algorithms
  49.         sharding-algorithms:
  50.           ## 注意这里名称(例如msg-id)不能用下划线,会加载不了下面的参数导致启动报错
  51.           msg-id:
  52.             type: INLINE
  53.             props:
  54.                         ## 使用id取模算法
  55.               algorithm-expression: team_msg_${id % 2}
  56.          ## 读写分离相关                  
  57.       readwrite-splitting:
  58.         data-sources:
  59.           customer-ds:
  60.             load-balancer-name: customer-lb
  61.             static-strategy:
  62.               write-data-source-name: master
  63.               read-data-source-names: read,read2,write
  64.         load-balancers:
  65.             customer-lb:
  66.                             ## 使用自定义的复杂均衡算法
  67.                 type: CUSTOM
  68.     props:
  69.       # 显示处理之后的真实sql
  70.       sql-show: true
复制代码
四. 代码设置

最关键的设置就是必要把MybatisPlus的数据源注册为使用 shardingsphere-jdbc 的数据源,并且保证数据源的名称和原来MybatisPlus的数据源同等,shardingSphereDataSource里面实在有一个Map保存了application.yml中所有设置的数据源,这里重要是为了方便后续使用@DS做动态数据源切换,以是把同一个ShardingSphere的数据库注册为4个动态数据源,避免使用@DS找不到对应的数据源;
  1. import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
  2. import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
  3. import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
  4. import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
  5. import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.apache.shardingsphere.driver.jdbc.adapter.AbstractDataSourceAdapter;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.boot.SpringBootConfiguration;
  11. import org.springframework.boot.autoconfigure.AutoConfigureBefore;
  12. import org.springframework.context.annotation.Bean;
  13. import org.springframework.context.annotation.Configuration;
  14. import org.springframework.context.annotation.Lazy;
  15. import org.springframework.context.annotation.Primary;
  16. import javax.annotation.Resource;
  17. import javax.sql.DataSource;
  18. import java.util.Arrays;
  19. import java.util.HashMap;
  20. import java.util.Map;
  21. @Configuration
  22. @AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
  23. public class MyDataSourceConfiguration {
  24.     /**
  25.      * mybatisplus 动态数据源配置项
  26.      */
  27.     @Autowired
  28.     private DynamicDataSourceProperties properties;
  29.     /**
  30.      * shardingjdbc的数据源
  31.      */
  32.     @Lazy
  33.     @Resource(name = "shardingSphereDataSource")
  34.     private AbstractDataSourceAdapter shardingSphereDataSource;
  35.     @Value("${spring.sharding-sphere.datasource.names}")
  36.     private String shardingDataSourceNames;
  37.     /**
  38.      * 注册动态数据源  这里非常关键,因为我们需要用到@DS注解配置动态选择数据源,同上又要让选择的数据源使用shardingjdbc的数据源
  39.      * 所以,这里需要动态的把所有的数据源都注册为  shardingjdbc的数据源
  40.      */
  41.     @Bean
  42.     public DynamicDataSourceProvider dynamicDataSourceProvider() {
  43.         if (StringUtils.isBlank(shardingDataSourceNames)) {
  44.             throw new RuntimeException("配置 spring.sharding-sphere.datasource.names 不能为空");
  45.         }
  46.         String[] names = shardingDataSourceNames.split(",");
  47.         return new AbstractDataSourceProvider() {
  48.             @Override
  49.             public Map<String, DataSource> loadDataSources() {
  50.                 Map<String, DataSource> dataSourceMap = new HashMap<>();
  51.                 Arrays.stream(names).forEach(name -> dataSourceMap.put(name, shardingSphereDataSource));
  52.                 return dataSourceMap;
  53.             }
  54.         };
  55.     }
  56.     /**
  57.      * 将动态数据源设置为首选数据源
  58.      */
  59.     @Primary
  60.     @Bean
  61.     public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
  62.         DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
  63.         dataSource.setPrimary(properties.getPrimary());
  64.         dataSource.setStrict(properties.getStrict());
  65.         dataSource.setStrategy(properties.getStrategy());
  66.         dataSource.setProvider(dynamicDataSourceProvider);
  67.         dataSource.setP6spy(properties.getP6spy());
  68.         dataSource.setSeata(properties.getSeata());
  69.         return dataSource;
  70.     }
  71. }
复制代码
五. 自定义ShardingSphere中的复杂均衡算法

shardingsphere中的负载均衡必要实现ReadQueryLoadBalanceAlgorithm接口并在getType方法中返回自定义的算法名称,官方自带的又RoundRobinReadQueryLoadBalanceAlgorithm,RandomReadQueryLoadBalanceAlgorithm等,这里我们必须自定义算法才能兼容@DS注解实现自由切换数据源;
ShardingSphere使用的是SPI机制加载的,对应的加载源码部分如下:

以是如果我们要让自定义的ReadQueryLoadBalanceAlgorithm类生效,必要在项目中的 META-INF的services文件夹中创建org.apache.shardingsphere.readwritesplitting.spi.ReadQueryLoadBalanceAlgorithm 文件,并且把自定义的类填入该文件中
源码中的设置如下:

那么我们按照源码的设置直接在本身的项目中创建即可

最后自定义的CustomLoadBalanceAlgorithm 实现
  1. public class CustomLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {
  2.     private Properties props;
  3.     public CustomLoadBalanceAlgorithm() {
  4.     }
  5.     @Override
  6.     public void init(Properties props) {
  7.         this.props = props;
  8.     }
  9.     /**
  10.      * 获取数据源
  11.      *
  12.      * @param name  数据源名称(ShardingJDBC使用的)
  13.      * @param writeDataSourceName 写数据源名称
  14.      * @param readDataSourceNames 所有配置的复杂均衡中读数据源名称
  15.      * @param context 事务上下文对象,可以获取context.isInTransaction() 判断是否需要事务,可通过这个来判断是否使用 写数据源
  16.      * @return java.lang.String
  17.      */
  18.     @Override
  19.     public String getDataSource(String name, String writeDataSourceName, List<String> readDataSourceNames, TransactionConnectionContext context) {
  20.         // 获取当前MybatisPlus指定的数据源
  21.         String dsKey = DynamicDataSourceContextHolder.peek();
  22.         if (StringUtils.isNotBlank(dsKey)) {
  23.             if (writeDataSourceName.equals(dsKey)) {
  24.                 return dsKey;
  25.             }
  26.             if (readDataSourceNames.contains(dsKey)) {
  27.                 return dsKey;
  28.             }
  29.             throw new RuntimeException("@DS 配置错误,当前数据源[" + dsKey + "]不在SharingJDBC数据源列表[" + readDataSourceNames + "]中");
  30.         }
  31.         return writeDataSourceName;
  32.     }
  33.     @Override
  34.     public String getType() {
  35.         return "CUSTOM";
  36.     }
  37.     @Override
  38.     public boolean isDefault() {
  39.         return true;
  40.     }
  41.     @Override
  42.     @Generated
  43.     public Properties getProps() {
  44.         return this.props;
  45.     }
  46. }
复制代码
那么此时你的ShardingSphere就已经完全适配之前MybatisPlus动态数据源了

六. 源码

Gitee: https://gitee.com/luowenjie98/sharing-sphere-mybatisplus-demo
如果觉得对你有帮助,请给我点一个star,非常感谢 !

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表