Springboot整合shardingsphere和druid進行讀寫分離


最近在使用springboot整合shardingsphere和druid實現mysql數據庫讀寫分離時遇到了一些問題,特此記錄一下。

依賴版本

  • Springboot 2.1.6.RElEASE
  • shardingsphere 4.1.1
  • druid 1.1.23

需要的依賴如下:

<dependency>
	<groupId>org.apache.shardingsphere</groupId>
	<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
	<version>4.1.1</version>
</dependency>

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.1.23</version>
</dependency>

yml文件配置

datasource配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    druid:
      async-init: true
      keep-alive: true
      filters: stat,wall,logback  # 必須配置項,否則sql監控頁面沒有內容
      initial-size: 5
      max-active: 50
      min-idle: 5
      max-wait: 6000
      validation-query: SELECT 'x'
      test-on-borrow: false
      test-on-return: false
      test-while-idle: true
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      remove-abandoned: false
      log-abandoned: true
      filter:
        stat:
          enabled: true
          log-slow-sql: true
      web-stat-filter:
        enabled: true
        url-pattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
      stat-view-servlet:
        enabled: true # 控制是否開啟監控頁面
        url-pattern: /druid/*
        reset-enable: false
		# ip白名單,默認是127.0.0.1,為空時表示所有的ip都可以訪問,如果這里允許所有ip訪問必須配置為空,否則只能127.0.0.1訪問
        allow: 
		# ip黑名單,同理白名單
        deny:  
        login-username: druid  # 監控頁面登陸用戶名
        login-password: druid  # 監控頁面登陸密碼

讀寫分離的sharding配置

spring:
  shardingsphere:
    enabled: true # 是否啟用sharding,不啟用時使用datasource配置的數據源
    datasource:
      names: master,slave0 # 節點名稱,多個時使用逗號隔開
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.18.121.222:3306/spider?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        # 以下為druid配置,可以共用datasource中的druid配置,需要覆蓋時再重新配置
        filters: stat,wall,logback
        initial-size: 2
        max-active: 45
        min-idle: 6
      slave0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.18.121.222:3307/spider?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        # 以下為druid配置,可以共用datasource中的druid配置,需要覆蓋時再重新配置
        filters: stat,wall,logback
        initial-size: 3
        max-active: 20
        min-idle: 8
    masterslave:
      name: ms
      master-data-source-name: master
      slave-data-source-names: slave0
    props:
      sql:
        show: true

注意:
sharding數據源中的druid配置項基本可以使用spring.datasource.druid中的配置項,但是在實際使用過程中發現,filters: stat,wall,logback這項配置必須重新給各個數據源配置,不然sql監控頁面沒有任何內容展示。

遇到的問題

通過以上兩個步驟的配置,啟動沒有任何業務類的springboot項目,成功啟動,並且訪問druid監控頁面也正常,在滿懷激動的往正式項目中遷移后,發現項目啟動失敗,出現了下圖所示的異常:

這是什么鬼?以為配置出錯了,趕緊一頓檢查檢查配置,發現沒有任何問題,在多次嘗試無果后將配置還原發現又可以正常啟動從而基本確定是sharding和druid的配置導致了項目啟動失敗。

從sharding官網的FAQ中發現如下解釋:

根據官網的解釋以及從網上查的一些資料來看,解決方案有兩個:

  1. 去掉druid-spring-boot-starter,直接使用druid-xxx.jar來替代,這就不會出現兩個數據源沖突的問題
  2. 仍然使用druid-spring-boot-starter,但是在springboot的啟動類上exclude掉DruidDataSourceAutoConfigure這個類,忽略druid連接池的默認數據源配置(@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})

經過一番嘗試,以上兩種方案都可以解決啟動報錯的問題,但是使用上述兩種方案,即使配置了打開druid監控頁面的配置,訪問監控頁面時仍然是404,我的需求是要能監控數據庫的,因此上述兩種方案都不可行。

又要有監控頁面,又要項目正常啟動,一時陷入了僵局,后來在查找資料的過程中,發現可以通過手動創建數據源配置,並且將其指定為默認數據源就可以解決該問題,經過查到的資料以及公司大佬的支持,添加如下所示的配置類:

@Configuration
@EnableConfigurationProperties(JpaProperties.class)
public class DataSourceConfiguration {
	private final JpaProperties jpaProperties;

	private final Environment environment;

	public DataSourceConfiguration(JpaProperties jpaProperties, Environment environment) {
		this.jpaProperties = jpaProperties;
		this.environment = environment;
	}

	@Primary
	@Bean
	public DataSource dataSource() {
		String prefix = "spring.shardingsphere.datasource.";
		String each = this.getDataSourceNames(prefix).get(0);
		try {
			return this.getDataSource(prefix, each);
		} catch (final ReflectiveOperationException ex) {
			throw new ShardingSphereException("Can't find datasource type!", ex);
		}
	}

	@Primary
	@Bean
	public EntityManagerFactory entityManagerFactory() {
		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		vendorAdapter.setDatabase(Database.MYSQL);
		vendorAdapter.setGenerateDdl(true);
		vendorAdapter.setShowSql(true);
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
		factory.setJpaVendorAdapter(vendorAdapter);
		factory.setPersistenceUnitName("default");
		factory.setPackagesToScan("com.lzm.*");
		factory.setDataSource(this.dataSource());
		factory.setJpaPropertyMap(this.jpaProperties.getProperties());
		factory.afterPropertiesSet();
		return factory.getObject();
	}

	@Bean
	@Primary
	public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
		return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
	}

	@Primary
	@Bean
	public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
		JpaTransactionManager txManager = new JpaTransactionManager();
		txManager.setEntityManagerFactory(entityManagerFactory);
		return txManager;
	}

	private List<String> getDataSourceNames(final String prefix) {
		StandardEnvironment standardEnv = (StandardEnvironment) this.environment;
		standardEnv.setIgnoreUnresolvableNestedPlaceholders(true);
		return null == standardEnv.getProperty(prefix + "name")
				? new InlineExpressionParser(standardEnv.getProperty(prefix + "names")).splitAndEvaluate()
				: Collections.singletonList(standardEnv.getProperty(prefix + "name"));
	}

	@SuppressWarnings("unchecked")
	private DataSource getDataSource(final String prefix, final String dataSourceName) throws ReflectiveOperationException {
		Map dataSourceProps = PropertyUtil.handle(this.environment, prefix + dataSourceName.trim(), Map.class);
		Preconditions.checkState(!dataSourceProps.isEmpty(), "Wrong datasource properties!");
		DataSource result = DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);
		DataSourcePropertiesSetterHolder.getDataSourcePropertiesSetterByType(dataSourceProps.get("type").toString())
				.ifPresent(dataSourcePropertiesSetter -> dataSourcePropertiesSetter.propertiesSet(this.environment, prefix, dataSourceName, result));
		return result;
	}
}

以上打碼中根據sharding的配置手動創建數據源DataSource以及EntityManagerFactory等Bean,並且設置為默認加載的bean類型(@Primary),添加以上配置類后,重新啟動項目,項目正常啟動而且druid的監控頁面也可以正常訪問,此問題得到完美解決。具體源碼可以參考碼雲

參考文檔

1.JPA項目多數據源模式整合Sharding-jdbc實現數據脫敏
2.ardingSphere FAQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM