开发中使用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;

 
    org.springframework.boot 
    spring-boot-starter-actuator 

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 results = this.jdbcTemplate.query(validationQuery,
						new SingleColumnRowMapper());
				Object result = DataAccessUtils.requiredSingleResult(results);
				builder.withDetail("hello", result);
			}
			catch (Exception ex) {
				builder.down(ex);
			}
		}
	}


//... 
  

数据库检测时,主要是语句:

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

执行时报错,其中validationQuery=“SELECT 1” 。

问题可能原因是:sharding-jdbc 对于validationQuery=“SELECT 1”的语句不支持。(暂时未在官方文档上找到相关说明,后续进行验证)

sharding-jdbc官方网文档:http://shardingsphere.io/document/legacy/1.x/cn/00-overview/

 

三、解决方法:

1.利用Spring中同名Bean相互覆盖的特性,编写自定义HealthIndicators类 DbHealthIndicator 来覆盖掉原来的db检测bean。

该类主要实现HealthIndicator接口,提供一个health()方法的实现,并返回一个Health响应。Health响应需要包含status和可选的用于展示的详情:

@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;
    }
}

 

 

你可能感兴趣的:(经验分享)