DataSource health check failed
警告[错误]日志2020-06-05 10:48:56 WARN[-RMI TCP Connection(3)-192.168.0.152] [87] [org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator] : DataSource health check failedorg.springframework.dao.In
·
警告[错误]日志
2020-06-05 10:48:56 WARN [-RMI TCP Connection(3)-192.168.0.152] [87] [org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator] : DataSource health check failed
org.springframework.dao.InvalidDataAccessApiUsageException: ConnectionCallback; isValid; nested exception is java.sql.SQLFeatureNotSupportedException: isValid
at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:96)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:336)
at org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.isConnectionValid(DataSourceHealthIndicator.java:130)
at org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.doDataSourceHealthCheck(DataSourceHealthIndicator.java:116)
at org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.doHealthCheck(DataSourceHealthIndicator.java:100)
at org.springframework.boot.actuate.health.AbstractHealthIndicator.health(AbstractHealthIndicator.java:82)
at org.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37)
at org.springframework.boot.actuate.health.HealthEndpoint.getHealth(HealthEndpoint.java:71)
at org.springframework.boot.actuate.health.HealthEndpoint.getHealth(HealthEndpoint.java:39)
at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:99)
at org.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateHealth(HealthEndpointSupport.java:110)
at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:96)
at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:74)
at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:61)
at org.springframework.boot.actuate.health.HealthEndpoint.health(HealthEndpoint.java:65)
at org.springframework.boot.actuate.health.HealthEndpoint.health(HealthEndpoint.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282)
at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:77)
at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60)
at org.springframework.boot.actuate.endpoint.jmx.EndpointMBean.invoke(EndpointMBean.java:121)
at org.springframework.boot.actuate.endpoint.jmx.EndpointMBean.invoke(EndpointMBean.java:96)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
at sun.reflect.GeneratedMethodAccessor217.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:361)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.sql.SQLFeatureNotSupportedException: isValid
at org.apache.shardingsphere.shardingjdbc.jdbc.unsupported.AbstractUnsupportedOperationConnection.isValid(AbstractUnsupportedOperationConnection.java:157)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.jdbc.core.JdbcTemplate$CloseSuppressingInvocationHandler.invoke(JdbcTemplate.java:1525)
at com.sun.proxy.$Proxy194.isValid(Unknown Source)
at org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.isConnectionValid(DataSourceHealthIndicator.java:134)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:328)
... 46 common frames omitted
环境
- spring-boot-starter-parent 2.3.0.RELEASE
- spring-cloud-dependencies Hoxton.SR4
- spring-boot-actuator-autoconfigure 2.3.0.RELEASE.jar
- spring-boot-actuator 2.3.0.RELEASE.jar
详解
- 日志输出位置 在 AbstractHealthIndicator 的 87行 如下图
- 通过这里我们可以看到 是 doHealthCheck 发生的异常, 并捕获了异常,输出 warning 级别的警告日志
第一种解决方案 [修改日志级别,不推荐]
# 修改日志级别只是 隐藏了错误日志的输出,并没有解决根本问题,虽然该错误是warning级别,不影响项目使用的
login.level.org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator=error
- 之前使用 spring-boot-actuator 2.2.7.RELEASE spring-boot-actuator-autoconfigure 2.2.7.RELEASE
- 项目启动未出现改错误信息,故查看了一下 2.2.7.RELEASE版本的 DataSourceHealthIndicator 源码如下
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.IncorrectResultSetColumnCountException;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link HealthIndicator} that tests the status of a {@link DataSource} and optionally
* runs a test query.
*
* @author Dave Syer
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Arthur Kalimullin
* @since 2.0.0
*/
public class DataSourceHealthIndicator extends AbstractHealthIndicator implements InitializingBean {
private static final String DEFAULT_QUERY = "SELECT 1";
private DataSource dataSource;
private String query;
private JdbcTemplate jdbcTemplate;
/**
* Create a new {@link DataSourceHealthIndicator} instance.
*/
public DataSourceHealthIndicator() {
this(null, null);
}
/**
* Create a new {@link DataSourceHealthIndicator} using the specified
* {@link DataSource}.
* @param dataSource the data source
*/
public DataSourceHealthIndicator(DataSource dataSource) {
this(dataSource, null);
}
/**
* Create a new {@link DataSourceHealthIndicator} using the specified
* {@link DataSource} and validation query.
* @param dataSource the data source
* @param query the validation query to use (can be {@code null})
*/
public DataSourceHealthIndicator(DataSource dataSource, String query) {
super("DataSource health check failed");
this.dataSource = dataSource;
this.query = query;
this.jdbcTemplate = (dataSource != null) ? new JdbcTemplate(dataSource) : null;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(this.dataSource != null, "DataSource for DataSourceHealthIndicator must be specified");
}
@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);
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("result", result);
}
finally {
builder.withDetail("validationQuery", validationQuery);
}
}
private String getProduct() {
return this.jdbcTemplate.execute((ConnectionCallback<String>) this::getProduct);
}
private String getProduct(Connection connection) throws SQLException {
return connection.getMetaData().getDatabaseProductName();
}
protected String getValidationQuery(String product) {
String query = this.query;
if (!StringUtils.hasText(query)) {
DatabaseDriver specific = DatabaseDriver.fromProductName(product);
query = specific.getValidationQuery();
}
if (!StringUtils.hasText(query)) {
query = DEFAULT_QUERY;
}
return query;
}
/**
* Set the {@link DataSource} to use.
* @param dataSource the data source
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* Set a specific validation query to use to validate a connection. If none is set, a
* default validation query is used.
* @param query the query
*/
public void setQuery(String query) {
this.query = query;
}
/**
* Return the validation query or {@code null}.
* @return the query
*/
public String getQuery() {
return this.query;
}
/**
* {@link RowMapper} that expects and returns results from a single column.
*/
private static class SingleColumnRowMapper implements RowMapper<Object> {
@Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
int columns = metaData.getColumnCount();
if (columns != 1) {
throw new IncorrectResultSetColumnCountException(1, columns);
}
return JdbcUtils.getResultSetValue(rs, 1);
}
}
}
- 这里需要注意的是 doDataSourceHealthCheck 和 getValidationQuery 两个方法,下图给出对比
- 图一 2.2.7 版本 [图中的版本号标记错了]
- 图二 2.3.0 版本
- 在2.3.0 版本中的 query 是如果得到值的喃?? 如下
通过构造参数 我们发现只有 DataSourceHealthContributorAutoConfiguration.createIndicator 方法 92行,query 值 才可能不为空值,
如上图,我们也在 DataSourceHealthContributorAutoConfiguration中发现了 getValidationQuery方法,
此处好像深藏玄机啊…
此处
- 此处我的项目采用 的dbcp2 连接池,没找到具体的配置 DataSourcePoolMetadata.validationQuery 的方法,__望找到的小伙伴给评论一下.十分感谢
第二种解决方法
- 继承 DataSourceHealthContributorAutoConfiguration 重写createIndicator 方法
package cn.zhangfusheng.base.server.config;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {
@Value("${spring.datasource.dbcp2.validation-query:select 1}")
private String defaultQuery;
public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
super(dataSources, metadataProviders);
}
@Override
protected AbstractHealthIndicator createIndicator(DataSource source) {
DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source);
if (!StringUtils.hasText(indicator.getQuery())) {
indicator.setQuery(defaultQuery);
}
return indicator;
}
}
重新启动 项目 nice
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献5条内容
所有评论(0)