前言
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字段和TypeBasedSPI
的getType
方法返回值,确定实际使用的实现类。
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);
}
总结
ShardingRuleConfiguration
和TableRuleConfiguration
是ShardingJDBC中最重要的两个配置类,对应运行时的ShardingRule
和TableRule
。ShardingRuleConfiguration
可以配置默认分库分表策略,TableRuleConfiguration
可以对表做定制分库分表策略。- 通过SPI机制,用户可以对一些预留的扩展点做定制化开发。如脱敏规则、主键策略等。