简体   繁体   English

覆盖 Spring 启动运行状况端点

[英]Override Spring boot health endpoint

I have 2 services A & B. A is in front of B in deployment and has exact same API as B and just proxies requests to B and returns responses back from B to the caller.我有 2 个服务 A 和 B。A 在部署中位于 B 的前面,并且具有与 B 完全相同的 API,只是代理对 B 的请求并将响应从 B 返回给调用者。 B has many other services connected to it and its health endpoint shows health of all those services along with its own. B 连接了许多其他服务,其运行状况端点显示所有这些服务及其自身的运行状况。

A & B are both spring boot 2.2.6 services. A & B 都是 spring boot 2.2.6 服务。

Requirement : When the health endpoint of A is called (which is same as that of B) it should return the health exactly as shown by B. Basically pass the health request to B and return B's response back to the caller.要求:当 A 的健康端点被调用时(与 B 的相同),它应该完全按照 B 所示返回健康。基本上将健康请求传递给 B 并将 B 的响应返回给调用者。

So the question is how do we override the Spring managed actuator health endpoint of A so that instead of it showing its health it calls B and returns the response.所以问题是我们如何覆盖 A 的 Spring 管理的执行器运行状况端点,以便它调用 B 并返回响应而不是显示其运行状况。 Standard spring boot health indicator wont work in this case of course.在这种情况下,标准弹簧靴健康指示器当然不起作用。

So far its seems that class annotated with @EndpointWebExtension(endpoint = HealthEndpoint.class) can somehow be used to override the default health endpoints behavior however I am getting the following exception :到目前为止,用@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

So how do we override the health endpoint?那么我们如何覆盖健康端点? Any example, documentation link etc would helpful.任何示例、文档链接等都会有所帮助。

Update/Edit:更新/编辑:

Overriding the default HealthWebExtension seems tricky.覆盖默认的 HealthWebExtension 似乎很棘手。 So I found a simple workaround.所以我找到了一个简单的解决方法。 Let me know your thoughts on it: I simply disabled springs default healthendpoint in the application.properties file by using the property : management.endpoint.health.enabled=false and provide my own restcontroller with @GetMapping("/manage/health") which basically calls the service B and returns the health response.让我知道您对此的看法:我只是通过使用属性在application.properties文件中禁用了 springs 默认 healthendpoint : management.endpoint.health.enabled=false并使用@GetMapping("/manage/health")提供我自己的 restcontroller它基本上调用服务 B 并返回健康响应。

I don't quiet like this solution because first of all its a bit misleading that the health is disabled in the properties file and still the endpoint works.我不喜欢这个解决方案,因为首先它有点误导属性文件中的健康被禁用并且端点仍然工作。 Second if I make the property true it simply overrides my endpoint without any error or warning.其次,如果我使属性为真,它只会覆盖我的端点,而不会出现任何错误或警告。

Is there a way to make spring boot pick my custom restcontroller, if the health property is set to true instead of the default one?如果 health 属性设置为 true 而不是默认值,有没有办法让 spring boot 选择我的自定义 restcontroller?

What about the case where service A is down?服务A宕机了怎么办? You make a call to service A and the response does not include the status of A. There is a better way to go by implementing the HealthIndicator interface.您调用服务 A 并且响应不包括 A 的状态。通过实现HealthIndicator接口有更好的方法。 Something like :就像是 :

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

With this solution, you will get both : A and B health status.使用此解决方案,您将同时获得:A 和 B 健康状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM