Get health from composite indicators (#4074)
This commit is contained in:
committed by
GitHub
parent
eafecd3b6a
commit
1efa843ed8
@@ -19,19 +19,21 @@ package org.springframework.cloud.netflix.eureka;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.netflix.appinfo.HealthCheckHandler;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthContributor;
|
||||
import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.NamedContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
@@ -58,6 +60,7 @@ import org.springframework.util.Assert;
|
||||
* @author Spencer Gibb
|
||||
* @author Nowrin Anwar Joyita
|
||||
* @author Bertrand Renuart
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @see HealthCheckHandler
|
||||
* @see StatusAggregator
|
||||
*/
|
||||
@@ -77,14 +80,14 @@ public class EurekaHealthCheckHandler
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private Map<String, HealthIndicator> healthIndicators = new HashMap<>();
|
||||
private Map<String, HealthContributor> healthContributors = new HashMap<>();
|
||||
|
||||
/**
|
||||
* {@code true} until the context is stopped.
|
||||
*/
|
||||
private boolean running = true;
|
||||
|
||||
private Map<String, ReactiveHealthIndicator> reactiveHealthIndicators = new HashMap<>();
|
||||
private Map<String, ReactiveHealthContributor> reactiveHealthContributors = new HashMap<>();
|
||||
|
||||
public EurekaHealthCheckHandler(StatusAggregator statusAggregator) {
|
||||
this.statusAggregator = statusAggregator;
|
||||
@@ -99,39 +102,28 @@ public class EurekaHealthCheckHandler
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
final Map<String, HealthIndicator> healthIndicators = applicationContext.getBeansOfType(HealthIndicator.class);
|
||||
final Map<String, ReactiveHealthIndicator> reactiveHealthIndicators = applicationContext
|
||||
.getBeansOfType(ReactiveHealthIndicator.class);
|
||||
|
||||
populateHealthIndicators(healthIndicators);
|
||||
populateReactiveHealthIndicators(reactiveHealthIndicators);
|
||||
populateHealthContributors(applicationContext.getBeansOfType(HealthContributor.class));
|
||||
reactiveHealthContributors.putAll(applicationContext.getBeansOfType(ReactiveHealthContributor.class));
|
||||
}
|
||||
|
||||
void populateHealthIndicators(Map<String, HealthIndicator> healthIndicators) {
|
||||
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
|
||||
void populateHealthContributors(Map<String, HealthContributor> healthContributors) {
|
||||
for (Map.Entry<String, HealthContributor> entry : healthContributors.entrySet()) {
|
||||
// ignore EurekaHealthIndicator and flatten the rest of the composite
|
||||
// otherwise there is a never ending cycle of down. See gh-643
|
||||
if (entry.getValue() instanceof DiscoveryCompositeHealthContributor) {
|
||||
DiscoveryCompositeHealthContributor indicator = (DiscoveryCompositeHealthContributor) entry.getValue();
|
||||
indicator.forEach(contributor -> {
|
||||
if (!(contributor.getContributor() instanceof EurekaHealthIndicator)) {
|
||||
this.healthIndicators.put(contributor.getName(),
|
||||
(HealthIndicator) contributor.getContributor());
|
||||
indicator.getIndicators().forEach((name, discoveryHealthIndicator) -> {
|
||||
if (!(discoveryHealthIndicator instanceof EurekaHealthIndicator)) {
|
||||
this.healthContributors.put(name, (HealthIndicator) discoveryHealthIndicator::health);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.healthIndicators.put(entry.getKey(), entry.getValue());
|
||||
this.healthContributors.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void populateReactiveHealthIndicators(Map<String, ReactiveHealthIndicator> reactiveHealthIndicators) {
|
||||
for (Map.Entry<String, ReactiveHealthIndicator> entry : reactiveHealthIndicators.entrySet()) {
|
||||
this.reactiveHealthIndicators.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceStatus getStatus(InstanceStatus instanceStatus) {
|
||||
if (running) {
|
||||
@@ -151,21 +143,39 @@ public class EurekaHealthCheckHandler
|
||||
}
|
||||
|
||||
protected Status getStatus(StatusAggregator statusAggregator) {
|
||||
Status status;
|
||||
|
||||
Set<Status> statusSet = new HashSet<>();
|
||||
if (healthIndicators != null) {
|
||||
statusSet.addAll(healthIndicators.values().stream().map(HealthIndicator::health).map(Health::getStatus)
|
||||
.collect(Collectors.toSet()));
|
||||
for (HealthContributor contributor : healthContributors.values()) {
|
||||
processContributor(statusSet, contributor);
|
||||
}
|
||||
|
||||
if (reactiveHealthIndicators != null) {
|
||||
statusSet.addAll(reactiveHealthIndicators.values().stream().map(ReactiveHealthIndicator::health)
|
||||
.map(Mono::block).filter(Objects::nonNull).map(Health::getStatus).collect(Collectors.toSet()));
|
||||
for (ReactiveHealthContributor contributor : reactiveHealthContributors.values()) {
|
||||
processContributor(statusSet, contributor);
|
||||
}
|
||||
return statusAggregator.getAggregateStatus(statusSet);
|
||||
}
|
||||
|
||||
status = statusAggregator.getAggregateStatus(statusSet);
|
||||
return status;
|
||||
private void processContributor(Set<Status> statusSet, HealthContributor contributor) {
|
||||
if (contributor instanceof CompositeHealthContributor) {
|
||||
for (NamedContributor<HealthContributor> contrib : (CompositeHealthContributor) contributor) {
|
||||
processContributor(statusSet, contrib.getContributor());
|
||||
}
|
||||
}
|
||||
else if (contributor instanceof HealthIndicator) {
|
||||
statusSet.add(((HealthIndicator) contributor).health().getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void processContributor(Set<Status> statusSet, ReactiveHealthContributor contributor) {
|
||||
if (contributor instanceof CompositeReactiveHealthContributor) {
|
||||
for (NamedContributor<ReactiveHealthContributor> contrib : (CompositeReactiveHealthContributor) contributor) {
|
||||
processContributor(statusSet, contrib.getContributor());
|
||||
}
|
||||
}
|
||||
else if (contributor instanceof ReactiveHealthIndicator) {
|
||||
Health health = ((ReactiveHealthIndicator) contributor).health().block();
|
||||
if (health != null) {
|
||||
statusSet.add(health.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected InstanceStatus mapToInstanceStatus(Status status) {
|
||||
|
||||
@@ -16,18 +16,23 @@
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthContributor;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.NamedContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.SimpleStatusAggregator;
|
||||
import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicator;
|
||||
@@ -44,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
* @author Nowrin Anwar Joyita
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
class EurekaHealthCheckHandlerTests {
|
||||
|
||||
@@ -63,7 +69,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAllUp() throws Exception {
|
||||
void testAllUp() {
|
||||
initialize(UpHealthConfiguration.class, ReactiveUpHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
|
||||
@@ -71,7 +77,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHealthCheckNotReturnedWhenStopped() throws Exception {
|
||||
void testHealthCheckNotReturnedWhenStopped() {
|
||||
initialize(UpHealthConfiguration.class);
|
||||
|
||||
healthCheckHandler.stop();
|
||||
@@ -84,7 +90,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDownWithBlockingIndicators() throws Exception {
|
||||
void testDownWithBlockingIndicators() {
|
||||
initialize(UpHealthConfiguration.class, DownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
|
||||
@@ -92,7 +98,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDownWithReactiveIndicators() throws Exception {
|
||||
void testDownWithReactiveIndicators() {
|
||||
initialize(UpHealthConfiguration.class, ReactiveDownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
|
||||
@@ -100,7 +106,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDownWhenBlockingIndicatorUpAndReactiveDown() throws Exception {
|
||||
void testDownWhenBlockingIndicatorUpAndReactiveDown() {
|
||||
initialize(ReactiveUpHealthConfiguration.class, DownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = this.healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
|
||||
@@ -108,7 +114,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDownWhenBlockingIndicatorDownAndReactiveUp() throws Exception {
|
||||
void testDownWhenBlockingIndicatorDownAndReactiveUp() {
|
||||
initialize(ReactiveUpHealthConfiguration.class, ReactiveDownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = this.healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
|
||||
@@ -116,7 +122,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknown() throws Exception {
|
||||
void testUnknown() {
|
||||
initialize(FatalHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
|
||||
@@ -124,15 +130,38 @@ class EurekaHealthCheckHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // FIXME: 3.0.0
|
||||
void testEurekaIgnored() throws Exception {
|
||||
void testEurekaIgnored() {
|
||||
initialize(EurekaDownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
|
||||
assertThat(status).isEqualTo(InstanceStatus.UP);
|
||||
}
|
||||
|
||||
private void initialize(Class<?>... configurations) throws Exception {
|
||||
@Test
|
||||
void testCompositeComponentsDown() {
|
||||
initialize(CompositeComponentsDownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
|
||||
assertThat(status).isEqualTo(InstanceStatus.DOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompositeComponentsUp() {
|
||||
initialize(CompositeComponentsUpHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
|
||||
assertThat(status).isEqualTo(InstanceStatus.UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompositeComponentsOneDown() {
|
||||
initialize(CompositeComponentsOneDownHealthConfiguration.class);
|
||||
|
||||
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
|
||||
assertThat(status).isEqualTo(InstanceStatus.DOWN);
|
||||
}
|
||||
|
||||
private void initialize(Class<?>... configurations) {
|
||||
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(configurations);
|
||||
healthCheckHandler.setApplicationContext(applicationContext);
|
||||
healthCheckHandler.afterPropertiesSet();
|
||||
@@ -144,7 +173,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
public HealthIndicator healthIndicator() {
|
||||
return new AbstractHealthIndicator() {
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
builder.up();
|
||||
}
|
||||
};
|
||||
@@ -158,7 +187,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
public HealthIndicator healthIndicator() {
|
||||
return new AbstractHealthIndicator() {
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
builder.down();
|
||||
}
|
||||
};
|
||||
@@ -172,7 +201,7 @@ class EurekaHealthCheckHandlerTests {
|
||||
public HealthIndicator healthIndicator() {
|
||||
return new AbstractHealthIndicator() {
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
builder.status("fatal");
|
||||
}
|
||||
};
|
||||
@@ -238,4 +267,76 @@ class EurekaHealthCheckHandlerTests {
|
||||
|
||||
}
|
||||
|
||||
protected static class CompositeComponentsDownHealthConfiguration {
|
||||
|
||||
@Bean
|
||||
public CompositeHealthContributor compositeHealthContributor() {
|
||||
return new TestCompositeHealthContributor(InstanceStatus.DOWN, InstanceStatus.DOWN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class CompositeComponentsUpHealthConfiguration {
|
||||
|
||||
@Bean
|
||||
public CompositeHealthContributor compositeHealthContributor() {
|
||||
return new TestCompositeHealthContributor(InstanceStatus.UP, InstanceStatus.UP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class CompositeComponentsOneDownHealthConfiguration {
|
||||
|
||||
@Bean
|
||||
public CompositeHealthContributor compositeHealthContributor() {
|
||||
return new TestCompositeHealthContributor(InstanceStatus.UP, InstanceStatus.DOWN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestCompositeHealthContributor implements CompositeHealthContributor {
|
||||
|
||||
private final Map<String, HealthContributor> contributorMap = new HashMap<>();
|
||||
|
||||
TestCompositeHealthContributor(InstanceStatus firstContributorStatus, InstanceStatus secondContributorStatus) {
|
||||
contributorMap.put("first", new AbstractHealthIndicator() {
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
builder.status(firstContributorStatus.name());
|
||||
}
|
||||
});
|
||||
contributorMap.put("second", new AbstractHealthIndicator() {
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) {
|
||||
builder.status(secondContributorStatus.name());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthContributor getContributor(String name) {
|
||||
return contributorMap.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<NamedContributor<HealthContributor>> iterator() {
|
||||
Iterator<Map.Entry<String, HealthContributor>> iterator = contributorMap.entrySet().iterator();
|
||||
return new Iterator<NamedContributor<HealthContributor>>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedContributor<HealthContributor> next() {
|
||||
Map.Entry<String, HealthContributor> entry = iterator.next();
|
||||
return NamedContributor.of(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user