ShardingSphere + spring boot 读写分离,自定义负载均衡算法

ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,Sharding-JDBC是其中一个独立的产品,定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。我们使用Sharding-JDBC实现读写分离的功能

  • 适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。

ShardingSphere + spring boot 读写分离,自定义负载均衡算法_第1张图片

ShardingSphere + spring boot 读写分离,自定义负载均衡算法_第2张图片

官网地址: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

 

你可能感兴趣的:(ShardingSphere)