一、问题背景:
项目中(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
数据库检测时,主要是语句:
List
执行时报错,其中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;
}
}