ShardingJDBC源码阅读(一)配置

3,722 阅读6分钟

前言

Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款相互独立,却又能够混合部署配合使用的产品组成。 它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

  • 基于Apache ShardingSphere的ShardingJDBC的4.1.0版本源码分析
  • 官方案例来源
  • 目的
    • 通过本章源码分析,加深对ShardingJDBC配置与使用的理解
    • 为后续源码分析做铺垫

一、分片规则配置ShardingRuleConfiguration

1、官方案例

public final class ShardingDatabasesAndTablesConfigurationPrecise implements ExampleConfiguration {
    
    @Override
    public DataSource getDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        // order表分片策略
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        // order_item表分片策略
        shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
        // 绑定表 order - order_item
        shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
        // 广播表 t_address
        shardingRuleConfig.getBroadcastTables().add("t_address");
        // 默认分库策略
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "demo_ds_${user_id % 2}"));
        // 默认分表策略
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id", new PreciseModuloShardingTableAlgorithm()));
        // 创建DataSource
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new Properties());
    }
    // order表规则,没有配置分片规则,使用默认分片规则
    private static TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order", "demo_ds_${0..1}.t_order_${[0, 1]}");
        result.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_id", getProperties()));
        return result;
    }
    // order_item表规则,没有配置分片规则,使用默认分片规则
    private static TableRuleConfiguration getOrderItemTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order_item", "demo_ds_${0..1}.t_order_item_${[0, 1]}");
        result.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_item_id", getProperties()));
        return result;
    }
    // dataSourceMap
    private static Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("demo_ds_0", DataSourceUtil.createDataSource("demo_ds_0"));
        result.put("demo_ds_1", DataSourceUtil.createDataSource("demo_ds_1"));
        return result;
    }
    // 用于SNOWFLAKE主键生成的配置
    private static Properties getProperties() {
        Properties result = new Properties();
        result.setProperty("worker.id", "123");
        return result;
    }
}

2、ShardingRuleConfiguration

ShardingRuleConfiguration是最常用的配置类,支持分片配置、加密配置、基于主从的读写分离配置,实现RuleConfiguration标记接口。

@Getter
@Setter
public final class ShardingRuleConfiguration implements RuleConfiguration {
    private Collection<TableRuleConfiguration> tableRuleConfigs = new LinkedList<>();
    private Collection<String> bindingTableGroups = new LinkedList<>();
    private Collection<String> broadcastTables = new LinkedList<>();
    private String defaultDataSourceName;
    private ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig;
    private ShardingStrategyConfiguration defaultTableShardingStrategyConfig;
    private KeyGeneratorConfiguration defaultKeyGeneratorConfig;
    private Collection<MasterSlaveRuleConfiguration> masterSlaveRuleConfigs = new LinkedList<>();
    
    private EncryptRuleConfiguration encryptRuleConfig;
}
  • tableRuleConfigs:表规则配置,可以针对不同的表设置不同的分片规则,也可以使用全局默认分片规则。
  • bindingTableGroups:绑定表,用于关联查询防止笛卡尔积。
  • broadcastTables:广播表,每个节点都存在的表,往往是一些码表、配置表,例如:t_order, t_order_item。
  • defaultDataSourceName:默认数据源名称。
  • defaultDatabaseShardingStrategyConfig:默认分库策略配置。
  • defaultTableShardingStrategyConfig:默认分表策略配置。
  • defaultKeyGeneratorConfig:默认主键生成配置。
  • masterSlaveRuleConfigs:主从规则配置。
  • encryptRuleConfig:加密规则配置。

3、TableRuleConfiguration

TableRuleConfiguration针对逻辑表(如t_order)而言,配置逻辑表实际对应的数据节点(如demo_ds_{0..1}.t_order_{[0, 1]})。另外也可以针对逻辑表配置特定的分片策略和主键生成策略。

@Getter
@Setter
public final class TableRuleConfiguration {
    private final String logicTable;
    private final String actualDataNodes;
    private ShardingStrategyConfiguration databaseShardingStrategyConfig;
    private ShardingStrategyConfiguration tableShardingStrategyConfig;
    private KeyGeneratorConfiguration keyGeneratorConfig;
    public TableRuleConfiguration(final String logicTable) {
        this(logicTable, null);
    }
    public TableRuleConfiguration(final String logicTable, final String actualDataNodes) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(logicTable), "LogicTable is required.");
        this.logicTable = logicTable;
        this.actualDataNodes = actualDataNodes;
    }
}
  • logicTable:逻辑表名。例如:t_order。
  • actualDataNodes:实际数据节点。例如:demo_ds_{0..1}.t_order_{[0, 1]},表示demo_ds_0库的t_order_0表和t_order_1表 与 demo_ds_1库的t_order_0表和t_order_1表。
  • databaseShardingStrategyConfig:分库策略配置。
  • tableShardingStrategyConfig:分表策略配置。
  • keyGeneratorConfig:主键生成策略配置。

二、分片策略配置ShardingStrategyConfiguration

ShardingStrategyConfiguration是个标记接口,里面啥也没有。这个策略配置可以针对分库,也可以针对分表

public interface ShardingStrategyConfiguration {
}

sharding-jdbc中有很多标记接口,方便透传,然后通过instanceof走不同的逻辑。例如ShardingStrategyFactory根据ShardingStrategyConfiguration的实际类型,创建ShardingStrategy

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ShardingStrategyFactory {
    public static ShardingStrategy newInstance(final ShardingStrategyConfiguration shardingStrategyConfig) {
        if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
            return new StandardShardingStrategy((StandardShardingStrategyConfiguration) shardingStrategyConfig);
        }
        if (shardingStrategyConfig instanceof InlineShardingStrategyConfiguration) {
            return new InlineShardingStrategy((InlineShardingStrategyConfiguration) shardingStrategyConfig);
        }
        if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
            return new ComplexShardingStrategy((ComplexShardingStrategyConfiguration) shardingStrategyConfig);
        }
        if (shardingStrategyConfig instanceof HintShardingStrategyConfiguration) {
            return new HintShardingStrategy((HintShardingStrategyConfiguration) shardingStrategyConfig);
        }
        return new NoneShardingStrategy();
    }
}

ShardingStrategyConfiguration代表分片策略配置,而运行时是将配置转换为ShardingStrategy实际的分片策略。

接下来看一下ShardingStrategyConfiguration的四个实现,这里忽略了NoneShardingStrategyConfiguration,因为就是个空实现。

1、InlineShardingStrategyConfiguration

InlineShardingStrategyConfiguration通过表达式(String)配置,实现分片策略配置。

@Getter
public final class InlineShardingStrategyConfiguration implements ShardingStrategyConfiguration {
	// 分片列
    private final String shardingColumn;
    // 分片算法表达式
    private final String algorithmExpression;
    public InlineShardingStrategyConfiguration(final String shardingColumn, final String algorithmExpression) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(shardingColumn), "ShardingColumn is required.");
        Preconditions.checkArgument(!Strings.isNullOrEmpty(algorithmExpression), "AlgorithmExpression is required.");
        this.shardingColumn = shardingColumn;
        this.algorithmExpression = algorithmExpression;
    }
}

官方案例:

new InlineShardingStrategyConfiguration("user_id", "demo_ds_${user_id % 2}")表示:针对user_id这列,如果user_id % 2 == 0,进入demo_ds_0数据源;如果user_id % 2 == 1,进入demo_ds_1数据源。

2、StandardShardingStrategyConfiguration

StandardShardingStrategyConfiguration通过配置PreciseShardingAlgorithm精确分片算法和RangeShardingAlgorithm区间分片算法(可选),实现分片策略配置。

@Getter
public final class StandardShardingStrategyConfiguration implements ShardingStrategyConfiguration {
    // 分片列
    private final String shardingColumn;
    // 精确分片算法
    private final PreciseShardingAlgorithm preciseShardingAlgorithm;
    // 区间分片算法
    private final RangeShardingAlgorithm rangeShardingAlgorithm;
    
    public StandardShardingStrategyConfiguration(final String shardingColumn, final PreciseShardingAlgorithm preciseShardingAlgorithm) {
        this(shardingColumn, preciseShardingAlgorithm, null);
    }
    
    public StandardShardingStrategyConfiguration(final String shardingColumn, final PreciseShardingAlgorithm preciseShardingAlgorithm, final RangeShardingAlgorithm rangeShardingAlgorithm) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(shardingColumn), "ShardingColumns is required.");
        Preconditions.checkNotNull(preciseShardingAlgorithm, "PreciseShardingAlgorithm is required.");
        this.shardingColumn = shardingColumn;
        this.preciseShardingAlgorithm = preciseShardingAlgorithm;
        this.rangeShardingAlgorithm = rangeShardingAlgorithm;
    }
}

PreciseShardingAlgorithm

PreciseShardingAlgorithm精确分片算法,针对单个值PreciseShardingValue进行分片,用于处理 = 或 in。

public interface PreciseShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<T> shardingValue);
}

PreciseShardingValue保存了逻辑表、列名和列对应的值。

public final class PreciseShardingValue<T extends Comparable<?>> implements ShardingValue {
    // 逻辑表
    private final String logicTableName;
    // 列名
    private final String columnName;
    // 值
    private final T value;
}

官方案例

根据表名是否以分片键除2取模结尾,确定落入哪个实际表。

public final class PreciseModuloShardingTableAlgorithm implements PreciseShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(final Collection<String> tableNames, final PreciseShardingValue<Long> shardingValue) {
        for (String each : tableNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }
}

RangeShardingAlgorithm

RangeShardingAlgorithm根据区间Range进行分片,用于处理between and、>、<。

public interface RangeShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<T> shardingValue);
}

RangeShardingValue保存了逻辑表名、列名、区间。

public final class RangeShardingValue<T extends Comparable<?>> implements ShardingValue {
    
    private final String logicTableName;
    
    private final String columnName;
    
    private final Range<T> valueRange;
}

官方案例

PreciseModuloShardingDatabaseAlgorithm根据user_id除2取模分库,RangeModuloShardingDatabaseAlgorithm根据user_id区间分表。

public final class ShardingDatabasesConfigurationRange implements ExampleConfiguration {
    
    @Override
    public DataSource getDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
        shardingRuleConfig.getBroadcastTables().add("t_address");
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("user_id"
                // 精确分片策略 user_id取模
                , new PreciseModuloShardingDatabaseAlgorithm()
                // 区间分片策略
                , new RangeModuloShardingDatabaseAlgorithm()));
        Properties properties = new Properties();
        properties.put("sql.show", true);
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, properties);
    }
    
    private static TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order");
        result.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_id", getProperties()));
        return result;
    }
    
    private static TableRuleConfiguration getOrderItemTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order_item");
        result.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_item_id", getProperties()));
        return result;
    }
    
    private static Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("demo_ds_0", DataSourceUtil.createDataSource("demo_ds_0"));
        result.put("demo_ds_1", DataSourceUtil.createDataSource("demo_ds_1"));
        return result;
    }
    
    private static Properties getProperties() {
        Properties result = new Properties();
        result.setProperty("worker.id", "123");
        return result;
    }
}
  • 如果[1,5]区间包含输入区间,则返回demo_ds_0数据源;
  • 如果[6,10]区间包含输入区间,则返回demo_ds_1数据源;
  • 如果[1,10]区间包含输入区间,则返回所有数据源;
  • 其他抛异常。
public final class RangeModuloShardingDatabaseAlgorithm implements RangeShardingAlgorithm<Integer> {
    @Override
    public Collection<String> doSharding(final Collection<String> databaseNames, final RangeShardingValue<Integer> shardingValueRange) {
        Set<String> result = new LinkedHashSet<>();
        if (Range.closed(1, 5).encloses(shardingValueRange.getValueRange())) {
            for (String each : databaseNames) {
                if (each.endsWith("0")) {
                    result.add(each);
                }
            }
        } else if (Range.closed(6, 10).encloses(shardingValueRange.getValueRange())) {
            for (String each : databaseNames) {
                if (each.endsWith("1")) {
                    result.add(each);
                }
            }
        } else if (Range.closed(1, 10).encloses(shardingValueRange.getValueRange())) {
            result.addAll(databaseNames);
        } else {
            throw new UnsupportedOperationException();
        }
        return result;
    }
}

这只是个案例演示,实际使用并不会这样配置。

这样配置的结果是,根据user_id % 2插入demo_ds_0和demo_ds_1,demo_ds_0里有2、4、6、8、10,demo_ds_1里有1、3、5、7、9。

如果SELECT i.* FROM t_order o, t_order_item i WHERE o.order_id = i.order_id AND o.user_id BETWEEN 1 AND 5只会查到user_id为2和4的数据。

3、HintShardingStrategyConfiguration

HintShardingStrategyConfiguration暗示分片策略配置。 HintShardingStrategyConfiguration里并没有指定分片建是哪一个字段。 暗示分片,不会通过解析sql中的字段对应的值来进行分片(也就是不会根据user_id = ?或user_id > ?,这个?代表的数值来执行分片算法),需要通过HintManager(ThreadLocal)设置分片值来执行分片算法。

@Getter
public final class HintShardingStrategyConfiguration implements ShardingStrategyConfiguration {
    
    private final HintShardingAlgorithm shardingAlgorithm;
    
    public HintShardingStrategyConfiguration(final HintShardingAlgorithm shardingAlgorithm) {
        Preconditions.checkNotNull(shardingAlgorithm, "ShardingAlgorithm is required.");
        this.shardingAlgorithm = shardingAlgorithm;
    }
}

HintShardingAlgorithm暗示分片算法接口。

public interface HintShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<T> shardingValue);
}

HintShardingValue保存了逻辑表名、列名和一个值列表,这个值列表是通过HintManager放入的。

public final class HintShardingValue<T extends Comparable<?>> implements ShardingValue {
    private final String logicTableName;
    private final String columnName;
    private final Collection<T> values;
}

官方案例

ModuloHintShardingAlgorithm还是根据除2取余来确定落入实际的表名或库名。

public final class ModuloHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final HintShardingValue<Long> shardingValue) {
        Collection<String> result = new ArrayList<>();
        for (String each : availableTargetNames) {
            for (Long value : shardingValue.getValues()) {
                if (each.endsWith(String.valueOf(value % 2))) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}

使用Hint分片,需要在使用前通过HintManager设置分片值,这个值在sharding-jdbc的处理过程中会放入HintShardingValue,传给ModuloHintShardingAlgorithm

private static void processWithHintValue(final DataSource dataSource) throws SQLException {
    try (HintManager hintManager = HintManager.getInstance();
         Connection connection = dataSource.getConnection();
         Statement statement = connection.createStatement()) {
        setHintValue(hintManager);
        statement.execute("select * from t_order");
        statement.execute("SELECT i.* FROM t_order o, t_order_item i WHERE o.order_id = i.order_id");
        statement.execute("select * from t_order_item");
        statement.execute("INSERT INTO t_order (user_id, address_id, status) VALUES (1, 1, 'init')");
    }
}
private static void setHintValue(final HintManager hintManager) {
	switch (TYPE) {
	    case DATABASE_TABLES:
	        hintManager.addDatabaseShardingValue("t_order", 1L);
	        hintManager.addTableShardingValue("t_order", 1L);
	        return;
	    case DATABASE_ONLY:
	        hintManager.setDatabaseShardingValue(1L);
	        return;
	    case MASTER_ONLY:
	        hintManager.setMasterRouteOnly();
	        return;
	    default:
	        throw new UnsupportedOperationException("unsupported type");
	}
}

更多Hint分片相关内容,后续再了解。

4、ComplexShardingStrategyConfiguration

ComplexShardingStrategyConfiguration复合分片策略配置。针对多个字段,设置ComplexKeysShardingAlgorithm分片算法,实现根据多分片键设置分片策略。

@Getter
public final class ComplexShardingStrategyConfiguration implements ShardingStrategyConfiguration {
    
    private final String shardingColumns;
    
    private final ComplexKeysShardingAlgorithm shardingAlgorithm;
    
    public ComplexShardingStrategyConfiguration(final String shardingColumns, final ComplexKeysShardingAlgorithm shardingAlgorithm) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(shardingColumns), "ShardingColumns is required.");
        Preconditions.checkNotNull(shardingAlgorithm, "ShardingAlgorithm is required.");
        this.shardingColumns = shardingColumns;
        this.shardingAlgorithm = shardingAlgorithm;
    }
}

ComplexKeysShardingAlgorithm根据ComplexKeysShardingValue执行分片。

public interface ComplexKeysShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<T> shardingValue);
}

ComplexKeysShardingValue保存了逻辑表名、字段-分片值的映射关系和字段-分片区间的映射关系。

@RequiredArgsConstructor
@Getter
@ToString
public final class ComplexKeysShardingValue<T extends Comparable<?>> implements ShardingValue {
    private final String logicTableName;
    private final Map<String, Collection<T>> columnNameAndShardingValuesMap;
    private final Map<String, Range<T>> columnNameAndRangeValuesMap;
}

三、MasterSlaveRuleConfiguration

MasterSlaveRuleConfiguration配置了主从规则。

@Getter
public final class MasterSlaveRuleConfiguration implements RuleConfiguration {
    
    private final String name;
    
    private final String masterDataSourceName;
    
    private final List<String> slaveDataSourceNames;
    
    private final LoadBalanceStrategyConfiguration loadBalanceStrategyConfiguration;
    
    public MasterSlaveRuleConfiguration(final String name, final String masterDataSourceName, final List<String> slaveDataSourceNames) {
        this(name, masterDataSourceName, slaveDataSourceNames, null);
    }
    
    public MasterSlaveRuleConfiguration(final String name, 
                                        final String masterDataSourceName, final List<String> slaveDataSourceNames, final LoadBalanceStrategyConfiguration loadBalanceStrategyConfiguration) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Name is required.");
        Preconditions.checkArgument(!Strings.isNullOrEmpty(masterDataSourceName), "MasterDataSourceName is required.");
        Preconditions.checkArgument(null != slaveDataSourceNames && !slaveDataSourceNames.isEmpty(), "SlaveDataSourceNames is required.");
        this.name = name;
        this.masterDataSourceName = masterDataSourceName;
        this.slaveDataSourceNames = slaveDataSourceNames;
        this.loadBalanceStrategyConfiguration = loadBalanceStrategyConfiguration;
    }
}
  • name:MasterSlaveRuleConfiguration的逻辑数据源名称。
  • masterDataSourceName:主库数据源名。
  • slaveDataSourceNames:主库对应的所有从库的数据源名。
  • loadBalanceStrategyConfiguration:主从的负载均衡策略。

官方案例

public final class ShardingMasterSlaveConfigurationPrecise implements ExampleConfiguration {
    
    @Override
    public DataSource getDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
        shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
        shardingRuleConfig.getBroadcastTables().add("t_address");
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", new PreciseModuloShardingDatabaseAlgorithm()));
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id", new PreciseModuloShardingTableAlgorithm()));
        shardingRuleConfig.setMasterSlaveRuleConfigs(getMasterSlaveRuleConfigurations());
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new Properties());
    }
    
    private static TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order", "ds_${0..1}.t_order_${[0, 1]}");
        result.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_id", getProperties()));
        return result;
    }
    
    private static TableRuleConfiguration getOrderItemTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order_item", "ds_${0..1}.t_order_item_${[0, 1]}");
        result.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "order_item_id", getProperties()));
        return result;
    }
    // 主从配置
    private static List<MasterSlaveRuleConfiguration> getMasterSlaveRuleConfigurations() {
        MasterSlaveRuleConfiguration masterSlaveRuleConfig1 = new MasterSlaveRuleConfiguration("ds_0", "demo_ds_master_0", Arrays.asList("demo_ds_master_0_slave_0", "demo_ds_master_0_slave_1"));
        MasterSlaveRuleConfiguration masterSlaveRuleConfig2 = new MasterSlaveRuleConfiguration("ds_1", "demo_ds_master_1", Arrays.asList("demo_ds_master_1_slave_0", "demo_ds_master_1_slave_1"));
        return Lists.newArrayList(masterSlaveRuleConfig1, masterSlaveRuleConfig2);
    }
    // 数据源配置
    private static Map<String, DataSource> createDataSourceMap() {
        final Map<String, DataSource> result = new HashMap<>();
        result.put("demo_ds_master_0", DataSourceUtil.createDataSource("demo_ds_master_0"));
        result.put("demo_ds_master_0_slave_0", DataSourceUtil.createDataSource("demo_ds_master_0_slave_0"));
        result.put("demo_ds_master_0_slave_1", DataSourceUtil.createDataSource("demo_ds_master_0_slave_1"));
        result.put("demo_ds_master_1", DataSourceUtil.createDataSource("demo_ds_master_1"));
        result.put("demo_ds_master_1_slave_0", DataSourceUtil.createDataSource("demo_ds_master_1_slave_0"));
        result.put("demo_ds_master_1_slave_1", DataSourceUtil.createDataSource("demo_ds_master_1_slave_1"));
        return result;
    }
    
    private static Properties getProperties() {
        Properties result = new Properties();
        result.setProperty("worker.id", "123");
        return result;
    }
}

四、基于SPI的配置

sharding-jdbc中使用了JDK的SPI机制,方便用户根据实际需求扩展。sharding-jdbc如何结合SPI,后续如果有机会再聊。

TypeBasedSPIConfiguration是配置基类,需要配置type(对应TypeBasedSPI接口获取的type)和properties。

public abstract class TypeBasedSPIConfiguration {
    private final String type;
    private final Properties properties;
    public TypeBasedSPIConfiguration(final String type) {
        this(type, null);
    }
    public TypeBasedSPIConfiguration(final String type, final Properties properties) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(type), "Type is required.");
        this.type = type;
        this.properties = null == properties ? new Properties() : properties;
    }
}

运行时,通过匹配TypeBasedSPIConfiguration的type字段和TypeBasedSPIgetType方法返回值,确定实际使用的实现类。

public interface TypeBasedSPI {
    String getType();
    Properties getProperties();
    void setProperties(Properties properties);
}

1、CenterConfiguration

CenterConfiguration是实现sharding-jdbc集成配置(注册)中心的配置。

@Getter
@Setter
public final class CenterConfiguration extends TypeBasedSPIConfiguration {
    // config_center表示配置中心,registry_center表示注册中心
    private String orchestrationType;
    // 配置(注册)中心地址列表
    private String serverLists;
    // namespace
    private String namespace;
    public CenterConfiguration(final String type) {
        super(type);
    }
    public CenterConfiguration(final String type, final Properties properties) {
        super(type, properties);
    }
}

CenterRepository是配置(注册)中心需要实现的接口,就是一些增、删、watch操作。

public interface CenterRepository extends TypeBasedSPI {
    void init(CenterConfiguration config);
    String get(String key);
    void persist(String key, String value);
    void close();
    List<String> getChildrenKeys(String key);
    void watch(String key, DataChangedEventListener dataChangedEventListener);
}

ConfigCenterRepository是仅提供配置中心功能的中间件需要实现的接口,简单继承了CenterRepository接口,没有扩展其他需要实现的方法。

public interface ConfigCenterRepository extends CenterRepository {
}

RegistryCenterRepository是不仅提供配置中心,且还能提供注册中心功能的中间件需要实现的接口,扩展了一个persistEphemeral方法。目前只有Zookeeper、Etcd实现了这个接口,对于Zookeeper就是创建临时节点。

public interface RegistryCenterRepository extends CenterRepository {
    void persistEphemeral(String key, String value);
}

目前支持Apollo、Nacos、Zookeeper、Etcd。对应的源码都在sharding-orchestration模块下,后续有机会再深入了解。

2、EncryptorRuleConfiguration

EncryptorRuleConfiguration配置支持字段的加密解密,例如对用户手机号脱敏等等,官方案例参照:org.apache.shardingsphere.example.encrypt.table.raw.jdbc.config.EncryptDatabasesConfiguration#getDataSource。

@Getter
public final class EncryptorRuleConfiguration extends TypeBasedSPIConfiguration {
    public EncryptorRuleConfiguration(final String type, final Properties properties) {
        super(type, properties);
    }
}

所有的加密算法需要实现Encryptor接口,提供加解密实现。

public interface Encryptor extends TypeBasedSPI {
    // 初始化
    void init();
    // 原文加密为密文
    String encrypt(Object plaintext);
    // 密文解密为原文
    Object decrypt(String ciphertext);
}

3、KeyGeneratorConfiguration

KeyGeneratorConfiguration用于配置主键生成策略,在ShardingRuleConfiguration配置中,可以设置全局默认,也可以针对某张表定制。

public final class KeyGeneratorConfiguration extends TypeBasedSPIConfiguration {
    private final String column;
    public KeyGeneratorConfiguration(final String type, final String column) {
        super(type);
        Preconditions.checkArgument(!Strings.isNullOrEmpty(column), "Column is required.");
        this.column = column;
    }
    public KeyGeneratorConfiguration(final String type, final String column, final Properties properties) {
        super(type, properties);
        Preconditions.checkArgument(!Strings.isNullOrEmpty(column), "Column is required.");
        this.column = column;
    }
}

可以看到KeyGeneratorConfiguration是针对某个字段配置的。

ShardingKeyGenerator是每个主键生成策略需要实现的接口,目前官方支持UUID和雪花算法两种主键生成策略。

public interface ShardingKeyGenerator extends TypeBasedSPI {
    Comparable<?> generateKey();
}

4、LoadBalanceStrategyConfiguration

LoadBalanceStrategyConfiguration在配置主从情况下,支持定义负载均衡策略。

@Getter
public final class LoadBalanceStrategyConfiguration extends TypeBasedSPIConfiguration {
    public LoadBalanceStrategyConfiguration(final String type) {
        super(type);
    }
    public LoadBalanceStrategyConfiguration(final String type, final Properties properties) {
        super(type, properties);
    }
}

所有负载均衡策略需要实现MasterSlaveLoadBalanceAlgorithm接口,实现getDataSource方法从主从数据源里选择一个数据源返回。

public interface MasterSlaveLoadBalanceAlgorithm extends TypeBasedSPI {
	// name - 主从数据源配置的name
	// masterDataSourceName - 主数据源名
	// slaveDataSourceNames - 从数据源名列表
    String getDataSource(String name, String masterDataSourceName, List<String> slaveDataSourceNames);
}

总结

  • ShardingRuleConfigurationTableRuleConfiguration是ShardingJDBC中最重要的两个配置类,对应运行时的ShardingRuleTableRule
  • ShardingRuleConfiguration可以配置默认分库分表策略,TableRuleConfiguration可以对表做定制分库分表策略。
  • 通过SPI机制,用户可以对一些预留的扩展点做定制化开发。如脱敏规则、主键策略等。