java - 覆盖 Spring 启动健康端点
问题描述
我有 2 个服务 A 和 B。A 在部署中位于 B 前面,并且具有与 B 完全相同的 API,只是代理对 B 的请求并将响应从 B 返回给调用者。B 有许多其他服务连接到它,它的健康端点显示所有这些服务的健康状况以及它自己的健康状况。
A & B 都是 spring boot 2.2.6 服务。
要求:当 A 的健康端点被调用时(与 B 的健康端点相同),它应该返回完全如 B 所示的健康。基本上将健康请求传递给 B,并将 B 的响应返回给调用者。
所以问题是我们如何覆盖 A 的 Spring 管理的执行器健康端点,以便它调用 B 并返回响应,而不是显示其健康状况。当然,标准弹簧靴健康指示器在这种情况下不起作用。
到目前为止,似乎带有注释的类@EndpointWebExtension(endpoint = HealthEndpoint.class)
可以以某种方式用于覆盖默认的健康端点行为,但是我得到了以下异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pathMappedEndpoints' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints]: Factory method 'pathMappedEndpoints' threw exception; nested exception is java.lang.IllegalStateException: Unable to map duplicate endpoint operations: [web request predicate GET to path 'health' produces: application/vnd.spring-boot.actuator.v3+json,application/vnd.spring-boot.actuator.v2+json,application/json] to healthEndpoint (applicationHealthWebEndpointExtension)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at com.gi_de.aon360.Aon360FeApplication.main(Aon360FeApplication.java:16)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints]: Factory method 'pathMappedEndpoints' threw exception; nested exception is java.lang.IllegalStateException: Unable to map duplicate endpoint operations: [web request predicate GET to path 'health' produces: application/vnd.spring-boot.actuator.v3+json,application/vnd.spring-boot.actuator.v2+json,application/json] to healthEndpoint (applicationHealthWebEndpointExtension)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651)
... 19 more
Caused by: java.lang.IllegalStateException: Unable to map duplicate endpoint operations: [web request predicate GET to path 'health' produces: application/vnd.spring-boot.actuator.v3+json,application/vnd.spring-boot.actuator.v2+json,application/json] to healthEndpoint (applicationHealthWebEndpointExtension)
at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.assertNoDuplicateOperations(EndpointDiscoverer.java:231)
at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.convertToEndpoint(EndpointDiscoverer.java:198)
at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.convertToEndpoints(EndpointDiscoverer.java:179)
at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124)
at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:116)
at org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints.lambda$getEndpoints$1(PathMappedEndpoints.java:69)
at java.util.LinkedHashMap$LinkedValues.forEach(LinkedHashMap.java:608)
at org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints.getEndpoints(PathMappedEndpoints.java:68)
at org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints.<init>(PathMappedEndpoints.java:63)
at org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration.pathMappedEndpoints(WebEndpointAutoConfiguration.java:111)
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.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 20 more
那么我们如何覆盖健康端点呢?任何示例、文档链接等都会有所帮助。
更新/编辑:
覆盖默认的 HealthWebExtension 似乎很棘手。所以我找到了一个简单的解决方法。让我知道您对此的想法:我只是application.properties
通过使用属性禁用了文件中的 springs 默认 healthendpoint :management.endpoint.health.enabled=false
并提供了我自己的 restcontroller@GetMapping("/manage/health")
基本上调用服务 B 并返回健康响应。
我不喜欢这个解决方案,因为首先它有点误导,在属性文件中禁用了运行状况并且端点仍然有效。其次,如果我将属性设为 true,它只会覆盖我的端点,而不会出现任何错误或警告。
如果health属性设置为true而不是默认值,有没有办法让spring boot选择我的自定义restcontroller?
解决方案
服务A宕机的情况怎么办?您调用服务 A 并且响应不包括 A 的状态。通过实现HealthIndicator
接口有更好的方法。就像是 :
@Component
public class ServiceBHealthIndicator implements HealthIndicator {
@Override
public Health health() {
ResponseEntity<Map> response = null;
try {
response = WebClient.create( "url to B/health").get()
.retrieve()
.toEntity( Map.class ).block();
} catch ( Exception e ) {
log.error( "B is down", e );
}
Health health = Health.down().withDetail( "reason", "Service B does not respond" ).build();
if ( Objects.nonNull( response ) && response.getStatusCode().is2xxSuccessful() ) {
health = Health.up()
.withDetail( "reason", "Service B is up" )
.withDetails( response.getBody() )
.build();
}
return health;
}
}
使用此解决方案,您将同时获得:A 和 B 健康状态。
推荐阅读
- python - 从python中的打印输出创建字典
- r - 为什么预测变量的 randomForest 排序在 OOB 错误率中很重要?
- azure-functions - 如何重用 .csx 文件
- c# - 碰撞器在降低玩家健康时无法正常工作
- buildroot - buildroot BR2 选项记录在哪里?
- java - AWS - 使用 getSomething 方法的 Lambda 函数。如何创建正确的请求
- c# - c# Udp BeginReceive - 消息乱序
- r - R-地理编码 => 很多 NA
- java - 将Java代码组织成包等于架构模式/风格?
- mongodb - 无法启动 MongoDB,mongod 总是停止/等待