0 序
- 背景:项目中引入了
spring-boot-starter-actuator
健康检测模块
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1 问题描述
- 确认微服务刚启动后,因K8S集群周期性定时(默认每隔10s)健康检查微服务,导致报如下错误:
...
[TID: N/A] [xxx-service] [system] [2024/03/11 16:08:53.610] [INFO ] [main] [XXXXServiceBizApplication] main:59__||__ server startup - end
[TID: Ignored_Trace] [xxx-service] [system] [2024/03/11 16:08:59.186] [INFO ] [http-nio-9527-exec-1] [DirectJDKLog] log:173__||__Initializing Spring DispatcherServlet 'dispatcherServlet'
[TID: Ignored_Trace] [xxxx-service] [system] [2024/03/11 16:08:59.679] [INFO ] [http-nio-9527-exec-1] [HikariDataSource] getConnection$original$aVZjWOBW:110__|N/A(xxxx-service)|__HikariPool-1 - Starting...
[TID: Ignored_Trace] [xxxx-service] [system] [2024/03/11 16:08:59.788] [INFO ] [http-nio-9527-exec-1] [HikariDataSource] getConnection$original$aVZjWOBW:123__|N/A(xxxx-service)|__HikariPool-1 - Start completed.
[TID: Ignored_Trace] [xxxx-service] [system] [2024/03/11 16:08:59.895] [WARN ] [http-nio-9527-exec-1] [AbstractHealthIndicator] health:87__|N/A(xxxx-service)|__Redis health check failed
org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Failed connecting to host localhost:6379
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:282) ~[spring-data-redis-2.3.9.RELEASE.jar!/:2.3.9.RELEASE]
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:476) ~[spring-data-redis-2.3.9.RELEASE.jar!/:2.3.9.RELEASE]
at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:134) ~[spring-data-redis-2.3.9.RELEASE.jar!/:2.3.9.RELEASE]
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:97) ~[spring-data-redis-2.3.9.RELEASE.jar!/:2.3.9.RELEASE]
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:84) ~[spring-data-redis-2.3.9.RELEASE.jar!/:2.3.9.RELEASE]
at org.springframework.boot.actuate.redis.RedisHealthIndicator.doHealthCheck(RedisHealthIndicator.java:51) ~[spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.AbstractHealthIndicator.health(AbstractHealthIndicator.java:82) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:85) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:44) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:99) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateHealth(HealthEndpointSupport.java:110) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:96) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:74) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:61) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:71) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:60) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_402]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_402]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_402]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_402]
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) [spring-core-5.2.15.RELEASE.jar!/:5.2.15.RELEASE]
at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:77) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$ServletWebOperationAdapter.handle(AbstractWebMvcEndpointHandlerMapping.java:305) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(AbstractWebMvcEndpointHandlerMapping.java:388) [spring-boot-actuator-2.3.12.RELEASE.jar!/:2.3.12.RELEASE]
...
疑问:没有配置 localhost:6379 的redis数据源,哪来的 redis 连接信息呢?
- 已检查确认:工程中没有配置任何关于 REDIS 的数据源
- Spring Boot 版本 : 2.3.12.RELEASE
2 问题分析
通过spring-boot-actuator-autoconfigure:2.3.12.RELEASE
的jar
包找到了源码:
2.1 RedisHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.redis.RedisHealthContributorAutoConfiguration
/*
* 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.autoconfigure.redis;
import java.util.Map;
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.redis.RedisHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link RedisHealthIndicator}.
*
* @author Christian Dupuis
* @author Richard Santana
* @author Stephane Nicoll
* @author Mark Paluch
* @since 2.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisConnectionFactory.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnEnabledHealthIndicator("redis") // 关键代码 : management.health.redis
@AutoConfigureAfter({ RedisAutoConfiguration.class, RedisReactiveHealthContributorAutoConfiguration.class })
public class RedisHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<RedisHealthIndicator, RedisConnectionFactory> {
@Bean
@ConditionalOnMissingBean(name = { "redisHealthIndicator", "redisHealthContributor" })//关键代码
public HealthContributor redisHealthContributor(Map<String, RedisConnectionFactory> redisConnectionFactories) {
return createContributor(redisConnectionFactories);
}
}
2.2 ConditionalOnEnabledHealthIndicator
org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator
/*
* 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.autoconfigure.health;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional @Conditional} that checks whether or not a default health indicator
* is enabled. Matches if the value of the {@code management.health.<name>.enabled}
* property is {@code true}. Otherwise, matches if the value of the
* {@code management.health.defaults.enabled} property is {@code true} or if it is not
* configured.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnEnabledHealthIndicatorCondition.class) //依赖: OnEnabledHealthIndicatorCondition
public @interface ConditionalOnEnabledHealthIndicator {
/**
* The name of the health indicator.
* @return the name of the health indicator
*/
String value();
}
2.3 OnEnabledHealthIndicatorCondition
org.springframework.boot.actuate.autoconfigure.health.OnEnabledHealthIndicatorCondition
/*
* 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.autoconfigure.health;
import org.springframework.boot.actuate.autoconfigure.OnEndpointElementCondition;
import org.springframework.context.annotation.Condition;
/**
* {@link Condition} that checks if a health indicator is enabled.
*
* @author Stephane Nicoll
*/
class OnEnabledHealthIndicatorCondition extends OnEndpointElementCondition {
OnEnabledHealthIndicatorCondition() {
super("management.health.", ConditionalOnEnabledHealthIndicator.class);//配置前缀: management.health.
}
}
3 解决方法
方法1:在配置文件中禁用redis健康检查
通过@ConditionalOnEnabledHealthIndicator
可以知道解决办法,在配置文件中禁用redis检查。 【本方法,亲测有效】
management:
health:
# reference-doc
# https://www.cnblogs.com/yl97/p/14926029.html
# {@link org.springframework.boot.actuate.autoconfigure.health.OnEnabledHealthIndicatorCondition }
# org.springframework.boot.actuate.autoconfigure.redis.RedisHealthContributorAutoConfiguration
redis: # false : 禁用 redis 检查
enabled: false
方法2:自定义配置Class/Bean("RedisHealthIndicator")
查看源码发现RedisHealthIndicator
继承AbstractHealthIndicator
,AbstractHealthIndicator
实现了HealthIndicator
接口。即:我们可通过自定义配置来解决。
@Configuration
public class RedisHealthIndicator implements HealthIndicator{
@Override
public Health health() {
return Health.up().build();
}
}
特别注意:必须要将原有的RedisHealthIndicator类覆盖,所以class名为RedisHealthIndicator。如果类想另起名则需指定bean名为redisHealthIndicator,否则,(有网友反馈)还是会出现错误信息。