ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,Sharding-JDBC是其中一个独立的产品,定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。我们使用Sharding-JDBC实现读写分离的功能
官网地址:https://github.com/apache/incubator-shardingsphere
我们这次主要介绍使用Sharding-JDBC实现读写分离的功能配置时遇到的一些问题,读写分离的配置如下:
spring.shardingsphere.datasource.names=master,slave0,slave1
spring.shardingsphere.datasource.master.type=org.apache.commons.dbcp.BasicDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/master
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=
spring.shardingsphere.datasource.slave0.type=org.apache.commons.dbcp.BasicDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.url=jdbc:mysql://localhost:3306/slave0
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=
spring.shardingsphere.datasource.slave1.type=org.apache.commons.dbcp.BasicDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.url=jdbc:mysql://localhost:3306/slave1
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=
spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin
spring.shardingsphere.masterslave.name=ms
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0,slave1
spring.shardingsphere.props.sql.show=true
spring.shardingsphere.sharding.master-slave-rules..master-data-source-name= #主库数据源名称
spring.shardingsphere.sharding.master-slave-rules..slave-data-source-names[0]= #从库数据源名称列表
spring.shardingsphere.sharding.master-slave-rules..slave-data-source-names[1]= #从库数据源名称列表
spring.shardingsphere.sharding.master-slave-rules..slave-data-source-names[x]= #从库数据源名称列表
spring.shardingsphere.sharding.master-slave-rules..load-balance-algorithm-class-name= #从库负载均衡算法类名称。该类需实现MasterSlaveLoadBalanceAlgorithm接口且提供无参数构造器
spring.shardingsphere.sharding.master-slave-rules..load-balance-algorithm-type= #从库负载均衡算法类型,可选值:ROUND_ROBIN,RANDOM。若`load-balance-algorithm-class-name`存在则设置为自己定义的type
spring.shardingsphere.props.sql.show= #是否开启SQL显示,默认值: false
spring.shardingsphere.props.executor.size= #工作线程数量,默认值: CPU核数
spring.shardingsphere.props.check.table.metadata.enabled= #是否在启动时检查分表元数据一致性,默认值: false
下面介绍如何使用自定义的负载均衡算法以及源码分析
源码的入口:SpringBootConfiguration
private final SpringBootMasterSlaveRuleConfigurationProperties masterSlaveProperties;
@Bean
public DataSource dataSource() throws SQLException {
if (null != masterSlaveProperties.getMasterDataSourceName()) {
return MasterSlaveDataSourceFactory.createDataSource(dataSourceMap, masterSlaveSwapper.swap(masterSlaveProperties), propMapProperties.getProps());
}
if (!encryptProperties.getEncryptors().isEmpty()) {
return EncryptDataSourceFactory.createDataSource(dataSourceMap.values().iterator().next(), encryptSwapper.swap(encryptProperties));
}
return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingSwapper.swap(shardingProperties), propMapProperties.getProps());
}
其中配置类SpringBootMasterSlaveRuleConfigurationProperties是我们将要设置的负载均衡算发的配置
@ConfigurationProperties(prefix = "spring.shardingsphere.masterslave")
public class SpringBootMasterSlaveRuleConfigurationProperties extends YamlMasterSlaveRuleConfiguration {
}
@Getter
@Setter
public class YamlMasterSlaveRuleConfiguration implements YamlConfiguration {
private String name;
private String masterDataSourceName;
private Collection slaveDataSourceNames = new ArrayList<>();
private String loadBalanceAlgorithmType;
}
核心代码masterSlaveSwapper.swap(masterSlaveProperties)
@Override
public MasterSlaveRuleConfiguration swap(final YamlMasterSlaveRuleConfiguration yamlConfiguration) {
return new MasterSlaveRuleConfiguration(yamlConfiguration.getName(),
yamlConfiguration.getMasterDataSourceName(), yamlConfiguration.getSlaveDataSourceNames(), getLoadBalanceStrategyConfiguration(yamlConfiguration));
}
private LoadBalanceStrategyConfiguration getLoadBalanceStrategyConfiguration(final YamlMasterSlaveRuleConfiguration yamlConfiguration) {
return Strings.isNullOrEmpty(yamlConfiguration.getLoadBalanceAlgorithmType()) ? null : new LoadBalanceStrategyConfiguration(yamlConfiguration.getLoadBalanceAlgorithmType());
}
我们配置了loadBalanceAlgorithmType,系统根据我们配置的type去实例化一个具体的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);
}
}
接着看MasterSlaveDataSourceFactory.createDataSource这个方法
public static DataSource createDataSource(final Map dataSourceMap, final MasterSlaveRuleConfiguration masterSlaveRuleConfig, final Properties props) throws SQLException {
return new MasterSlaveDataSource(dataSourceMap, masterSlaveRuleConfig, props);
}
public MasterSlaveDataSource(final Map dataSourceMap, final MasterSlaveRuleConfiguration masterSlaveRuleConfig, final Properties props) throws SQLException {
super(dataSourceMap);
cachedDatabaseMetaData = createCachedDatabaseMetaData(dataSourceMap);
this.masterSlaveRule = new MasterSlaveRule(masterSlaveRuleConfig);
shardingProperties = new ShardingProperties(null == props ? new Properties() : props);
}
核心接口为this.masterSlaveRule = new MasterSlaveRule(masterSlaveRuleConfig);
public MasterSlaveRule(final MasterSlaveRuleConfiguration config) {
name = config.getName();
masterDataSourceName = config.getMasterDataSourceName();
slaveDataSourceNames = config.getSlaveDataSourceNames();
loadBalanceAlgorithm = createMasterSlaveLoadBalanceAlgorithm(config.getLoadBalanceStrategyConfiguration());
masterSlaveRuleConfiguration = config;
}
loadBalanceAlgorithm = createMasterSlaveLoadBalanceAlgorithm(config.getLoadBalanceStrategyConfiguration());这个地方开始实例化我们自己的负载均衡策略
private MasterSlaveLoadBalanceAlgorithm createMasterSlaveLoadBalanceAlgorithm(final LoadBalanceStrategyConfiguration loadBalanceStrategyConfiguration) {
MasterSlaveLoadBalanceAlgorithmServiceLoader serviceLoader = new MasterSlaveLoadBalanceAlgorithmServiceLoader();
return null == loadBalanceStrategyConfiguration ? serviceLoader.newService() : serviceLoader.newService(loadBalanceStrategyConfiguration.getType(), loadBalanceStrategyConfiguration.getProperties());
}
public final class MasterSlaveLoadBalanceAlgorithmServiceLoader extends TypeBasedSPIServiceLoader {
static {
NewInstanceServiceLoader.register(MasterSlaveLoadBalanceAlgorithm.class);
}
public MasterSlaveLoadBalanceAlgorithmServiceLoader() {
super(MasterSlaveLoadBalanceAlgorithm.class);
}
}
关键的点在于MasterSlaveLoadBalanceAlgorithmServiceLoader这个类的静态代码块,使用的是ServiceLoader.load(service)将MasterSlaveLoadBalanceAlgorithm接口类的实现类注册到SERVICE_MAP,ServiceLoader.load涉及到的就是Java的SPI机制
/**
* Register SPI service into map for new instance.
*
* @param service service type
* @param type of service
*/
public static void register(final Class service) {
for (T each : ServiceLoader.load(service)) {
registerServiceClass(service, each);
}
}
@SuppressWarnings("unchecked")
private static void registerServiceClass(final Class service, final T instance) {
Collection> serviceClasses = SERVICE_MAP.get(service);
if (null == serviceClasses) {
serviceClasses = new LinkedHashSet<>();
}
serviceClasses.add(instance.getClass());
SERVICE_MAP.put(service, serviceClasses);
}
第一步:实现自定义的负载均衡算法,实现MasterSlaveLoadBalanceAlgorithm接口
@Component
@Getter
@Setter
@RequiredArgsConstructor
public final class LoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm {
private Properties properties = new Properties();
@Override
public String getType() {
return "loadBalance";
}
@Override
public String getDataSource(final String name, final String masterDataSourceName, final List slaveDataSourceNames) {
return slaveDataSourceNames.get(0);
}
}
第二步:将LoadBalanceAlgorithm通过SPI机智将其添加到MasterSlaveLoadBalanceAlgorithm 接口实现类列表,新增META-INF\services文件夹,在该文件夹下面,新建一个文件命名为org.apache.shardingsphere.spi.masterslave.MasterSlaveLoadBalanceAlgorithm(MasterSlaveLoadBalanceAlgorithm接口的全路径),文件内容为:
org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithm
org.apache.shardingsphere.core.strategy.masterslave.RandomMasterSlaveLoadBalanceAlgorithm
com.cxm.config.LoadBalanceAlgorithm
第三步:配置load-balance-algorithm-class-name和load-balance-algorithm-type属性
load-balance-algorithm-class-name: com.cxm.config.LoadBalanceAlgorithm
load-balance-algorithm-type: cxmLoadBalance