天天看點

springcloud整合seata+多資料源+sharding-jdbc

作者:鄭清

一、前言

本文通過以下環境整合Seata + 多資料源 + sharding-jdbc

  1. 1. spring-boot 2.7.0
  2. 2. spring-cloud 2021.0.2
  3. 3. spring-cloud-alibaba 2021.0.1.0
  4. 4. sharding-jdbc 4.1.1
  5. 5. seata-server 1.5.2
  6. 6. dynamic-datasource 3.3.2

二、docker-compose一鍵部署Seata

見 https://gitee.com/zhengqingya/docker-compose

三、項目整合

tips: 詳情見文末提供的源碼demo

1、引入依賴

<dependencies>
    <!-- sharding-jdbc -->
    <!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/sharding-jdbc-spring-boot-starter -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>4.1.1</version>
    </dependency>
    <!-- 整合seata -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-transaction-base-seata-at</artifactId>
        <version>4.1.1</version>
    </dependency>

    <!-- 動态資料源 -->
    <!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.3.2</version>
    </dependency>

    <!-- seata -->
    <!-- 最外層父pom.xml中統一管理seata版本 (全局修改版本為1.5.2) -->
    <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-seata -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter -->
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
    </dependency>
</dependencies>           

2、增加配置

spring:
  # 多資料源配置 可參考 https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    dynamic:
      primary: master # 設定預設的資料源或者資料源組,預設值即為master
      strict: false   # 設定嚴格模式,預設false不啟動. 啟動後在未比對到指定資料源時候會抛出異常,不啟動則使用預設資料源.
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        db-test:
          url: jdbc:mysql://127.0.0.1:3306/demo-bak?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver

      # 開啟seata代理,開啟後預設每個資料源都代理,如果某個不需要代理可單獨關閉
      seata: true
      seata-mode: at


  # sharding-jdbc配置
  shardingsphere:
    # 是否開啟SQL顯示
    props:
      sql:
        show: false
    # ====================== ↓↓↓↓↓↓ 資料源配置 ↓↓↓↓↓↓ ======================
    datasource:
      names: ds-master-0,ds-slave-0-1,ds-slave-0-2,ds-master-1,ds-slave-1-1,ds-slave-1-2
      # ====================== ↓↓↓↓↓↓ 配置第1個主從庫 ↓↓↓↓↓↓ ======================
      # 主庫1
      ds-master-0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/ds0?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
        username: root
        password: root
      # 主庫1-從庫1
      ds-slave-0-1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/ds0?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
        username: root
        password: root
      # 主庫1-從庫2
      ds-slave-0-2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/ds0?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
        username: root
        password: root
      # ====================== ↓↓↓↓↓↓ 配置第2個主從庫 ↓↓↓↓↓↓ ======================
      # 主庫2
      ds-master-1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/ds1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
        username: root
        password: root
      # 主庫2-從庫1
      ds-slave-1-1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/ds1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
        username: root
        password: root
      # 主庫2-從庫2
      ds-slave-1-2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/ds1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false # MySQL在高版本需要指明是否進行SSL連接配接 解決則加上 &useSSL=false
        username: root
        password: root

    sharding:
      # ====================== ↓↓↓↓↓↓ 讀寫分離配置 ↓↓↓↓↓↓ ======================
      master-slave-rules:
        ds-master-0:
          # 主庫
          masterDataSourceName: ds-master-0
          # 從庫
          slaveDataSourceNames:
            - ds-slave-0-1
            - ds-slave-0-2
          # 從庫查詢資料的負載均衡算法 目前有2種算法 round_robin(輪詢)和 random(随機)
          # 算法接口 org.apache.shardingsphere.spi.masterslave.MasterSlaveLoadBalanceAlgorithm
          # 實作類 RandomMasterSlaveLoadBalanceAlgorithm 和 RoundRobinMasterSlaveLoadBalanceAlgorithm
          loadBalanceAlgorithmType: ROUND_ROBIN
        ds-master-1:
          masterDataSourceName: ds-master-1
          slaveDataSourceNames:
            - ds-slave-1-1
            - ds-slave-1-2
          loadBalanceAlgorithmType: ROUND_ROBIN

      # ====================== ↓↓↓↓↓↓ 分庫分表配置 ↓↓↓↓↓↓ ======================
      tables:
        t_user:
          actual-data-nodes: ds-master-$->{0..1}.t_user$->{0..1}
          database-strategy:
            complex:
              sharding-columns: create_time
              algorithm-class-name: com.zhengqing.common.db.config.sharding.user.complex.MyDbComplexKeysShardingAlgorithm
          table-strategy:
            complex:
              sharding-columns: user_id
              algorithm-class-name: com.zhengqing.common.db.config.sharding.user.complex.MyTableComplexKeysShardingAlgorithm
        t_order:
          actual-data-nodes: ds-master-$->{0..1}.t_order$->{0..1}
          database-strategy:
            complex:
              sharding-columns: pay_time
              algorithm-class-name: com.zhengqing.common.db.config.sharding.order.complex.MyDbComplexKeysShardingAlgorithm
          table-strategy:
            complex:
              sharding-columns: user_id
              algorithm-class-name: com.zhengqing.common.db.config.sharding.order.complex.MyTableComplexKeysShardingAlgorithm

# seata配置
seata:
  # 是否開啟seata
  enabled: true
  # Seata 應用編号,預設為 ${spring.application.name}
  application-id: ${spring.application.name}
  # Seata 事務組編号,用于 TC 叢集名
  tx-service-group: test-tx-group
  # 是否開啟資料源代理
  enable-auto-data-source-proxy: false
  data-source-proxy-mode: AT
  # 服務配置項
  service:
    # 虛拟組和分組的映射
    vgroup-mapping:
      test-tx-group: default
    # 分組和 Seata 服務的映射
    grouplist:
      default: 127.0.0.1:8091
  config:
    type: nacos
    nacos:
      serverAddr: ${spring.cloud.nacos.config.server-addr}
      group: SEATA_GROUP
      namespace: ${spring.cloud.nacos.config.namespace}
      username: ${spring.cloud.nacos.config.username}
      password: ${spring.cloud.nacos.config.password}
      dataId: seata-server.properties
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: ${spring.cloud.nacos.config.server-addr}
      group: SEATA_GROUP
      namespace: ${spring.cloud.nacos.config.namespace}
      username: ${spring.cloud.nacos.config.username}
      password: ${spring.cloud.nacos.config.password}           

3、資料源配置

package com.zhengqing.common.db.config.dynamic;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.apache.shardingsphere.shardingjdbc.jdbc.adapter.AbstractDataSourceAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;

/**
 * <p> sharding-jdbc內建動态資料源配置 </p>
 *
 * @author zhengqingya
 * @description 使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解,切換資料源
 * ex: 切換為sharding-jdbc資料源 => @DS(DataSourceConfig.SHARDING_DATA_SOURCE_NAME)
 * @date 2021/11/2 10:13
 */
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class DataSourceConfig {
    /**
     * 分表資料源名稱
     */
    public static final String SHARDING_DATA_SOURCE_NAME = "sharding-data-source";
    /**
     * 動态資料源配置項
     */
    @Autowired
    private DynamicDataSourceProperties properties;

    /**
     * sharding-jdbc有四種資料源,需要根據業務注入不同的資料源
     * <p>
     * 1.未使用分片, 脫敏的名稱(預設): shardingDataSource;
     * 2.主從資料源: masterSlaveDataSource;
     * 3.脫敏資料源:encryptDataSource;
     * 4.影子資料源:shadowDataSource
     */
    @Lazy
    @Resource(name = "shardingDataSource")
    private AbstractDataSourceAdapter shardingDataSource;

    @Bean
    public DynamicDataSourceProvider dynamicDataSourceProvider() {
        Map<String, DataSourceProperty> datasourceMap = this.properties.getDatasource();
        return new AbstractDataSourceProvider() {
            @Override
            public Map<String, DataSource> loadDataSources() {
                Map<String, DataSource> dataSourceMap = this.createDataSourceMap(datasourceMap);
                // 将 shardingjdbc 管理的資料源也交給動态資料源管理
                dataSourceMap.put(SHARDING_DATA_SOURCE_NAME, DataSourceConfig.this.shardingDataSource);
                return dataSourceMap;
            }
        };
    }

    /**
     * 将動态資料源設定為首選的
     * 當spring存在多個資料源時, 自動注入的是首選的對象
     * 設定為主要的資料源之後,就可以支援sharding-jdbc原生的配置方式了
     */
    @Primary
    @Bean
    public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(this.properties.getPrimary());
        dataSource.setStrict(this.properties.getStrict());
        dataSource.setStrategy(this.properties.getStrategy());
        dataSource.setProvider(dynamicDataSourceProvider);
        dataSource.setP6spy(this.properties.getP6spy());
        dataSource.setSeata(this.properties.getSeata());
        return dataSource;
    }

}           

4、各個微服務下的resources目錄下準備seata.conf

eg:

client {
    application.id = user-server
    transaction.service.group = test-tx-group
}           

四、編寫業務代碼進行測試

使用:在對應的業務方法上加上相應的注解即可...

1、分庫分表

a:父服務聲明(主業務調用方)

@GlobalTransactional(rollbackFor = Exception.class)
@ShardingTransactionType(TransactionType.BASE)           

b:子服務聲明

@Transactional(rollbackFor = Exception.class)
@ShardingTransactionType(TransactionType.BASE)           

2、多資料源

單庫單表

@GlobalTransactional(rollbackFor = Exception.class)
@DS("db-test")           

五、源碼案例Demo

https://gitee.com/zhengqingya/java-workspace

今日分享語句: 耐心和恒心是學習的良藥,堅持不懈是成功的關鍵。