1. 程式人生 > >開發中使用Consul作為服務註冊中心時,專案接入sharding-jdbc後,db測活一致失敗問題解決

開發中使用Consul作為服務註冊中心時,專案接入sharding-jdbc後,db測活一致失敗問題解決

一、問題背景:

專案中(Spring boot)使用consul作為服務註冊中心時,當接入sharding-jdbc 1.4.x版本後,健康檢查一直失敗。主要是db檢測失敗,丟擲以下錯誤:

"db": { "status": "DOWN", "database": "MySQL", "error": "java.lang.NullPointerException: null" }

二、問題分析:

1.Consul中的測活依賴於spring-boot-starter-actuator包,不新增會check failing;

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-actuator</artifactId> 
</dependency>

2.在actuator包中下面HealthIndicators會被Spring Boot自動配置:

名字 描述
CassandraHealthIndicator 檢查Cassandra database是否正常
DiskSpaceHealthIndicator 低磁碟空間檢測
DataSourceHealthIndicator 檢查資料庫連線是否正常
ElasticsearchHealthIndicator 檢查Elasticsearch cluster是否正常
JmsHealthIndicator 檢查JMS broker是否正常
MailHealthIndicator 檢查mail server是否正常
MongoHealthIndicator 檢查Mongo database是否正常
RabbitHealthIndicator 檢查Rabbit server是否正常
RedisHealthIndicator 檢查Redis server是否正常
SolrHealthIndicator 檢查Solr server是否正常

3.該問題主要是在執行DataSourceHealthIndicator時,報錯。

dbHealthIndicator自動註冊的原始碼部分:
//org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration 類中定義
//....
@Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator dbHealthIndicator() {
   return createHealthIndicator(this.dataSources);
}

@Override
protected DataSourceHealthIndicator createHealthIndicator(DataSource source) {
   return new DataSourceHealthIndicator(source, getValidationQuery(source));
}

private String getValidationQuery(DataSource source) {
   DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
         .getDataSourcePoolMetadata(source);
   return (poolMetadata == null ? null : poolMetadata.getValidationQuery());
}

DataSourceHealthIndicator的資料庫檢測部分原始碼:

//類
//org.springframework.boot.actuate.health.DataSourceHealthIndicator

//...

@Override
	protected void doHealthCheck(Health.Builder builder) throws Exception {
		if (this.dataSource == null) {
			builder.up().withDetail("database", "unknown");
		}
		else {
			doDataSourceHealthCheck(builder);
		}
	}

	private void doDataSourceHealthCheck(Health.Builder builder) throws Exception {
		String product = getProduct();
		builder.up().withDetail("database", product);
		String validationQuery = getValidationQuery(product);
		if (StringUtils.hasText(validationQuery)) {
			try {
				// Avoid calling getObject as it breaks MySQL on Java 7
				List<Object> results = this.jdbcTemplate.query(validationQuery,
						new SingleColumnRowMapper());
				Object result = DataAccessUtils.requiredSingleResult(results);
				builder.withDetail("hello", result);
			}
			catch (Exception ex) {
				builder.down(ex);
			}
		}
	}


//...

資料庫檢測時,主要是語句:

List<Object> results = this.jdbcTemplate.query(validationQuery, new SingleColumnRowMapper());

執行時報錯,其中validationQuery=“SELECT 1” 。

問題可能原因是:sharding-jdbc 對於validationQuery=“SELECT 1”的語句不支援。(暫時未在官方文件上找到相關說明,後續進行驗證)

三、解決方法:

1.利用Spring中同名Bean相互覆蓋的特性,自定義HealthIndicators類 DbHealthIndicator 來替換掉原來的db檢測bean:

@Component
public class DbHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        int errorCode = check();
        if (errorCode != 0) {
            return Health.down().withDetail("Error Code", errorCode)  .build();
        }
        return Health.up().build();
    }

    int check(){
		//可以實現自定義的資料庫檢測邏輯
        return 0;
    }
}