Updates ConfigServerHealthIndicator.java for ConfigData (#1699)

ConfigClientAutoConfiguration now uses @ConditionalOnEnabledHealthIndicator("config") to enable the bean.

The configClient property source is now always added (even if empty) on successful call to config server.

The ConfigServerHealthIndicator now uses the existence of the configClient property source as the indicator that config server is UP. No new calls to config server are made by the health indicator.

Fixes gh-1696
This commit is contained in:
Spencer Gibb
2020-09-08 14:22:41 -04:00
committed by GitHub
parent 95f22177f2
commit 935560f91f
6 changed files with 69 additions and 69 deletions

View File

@@ -103,13 +103,16 @@ public abstract class AbstractConfigDataLoader<L extends AbstractConfigDataLocat
}
}
if (StringUtils.hasText(result.getState())
|| StringUtils.hasText(result.getVersion())) {
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Object> map = new HashMap<>();
if (StringUtils.hasText(result.getState())) {
putValue(map, "config.client.state", result.getState());
putValue(map, "config.client.version", result.getVersion());
composite.add(0, new MapPropertySource("configClient", map));
}
if (StringUtils.hasText(result.getVersion())) {
putValue(map, "config.client.version", result.getVersion());
}
// the existence of this property source confirms a successful
// response from config server
composite.add(0, new MapPropertySource("configClient", map));
return new ConfigData(composite);
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.config.client;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -25,6 +26,7 @@ import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
/**
@@ -53,22 +55,21 @@ public class ConfigClientAutoConfiguration {
return client;
}
@Bean
public ConfigClientHealthProperties configClientHealthProperties() {
return new ConfigClientHealthProperties();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnBean(ConfigServicePropertySourceLocator.class)
@ConditionalOnProperty(value = "health.config.enabled", matchIfMissing = true)
@ConditionalOnEnabledHealthIndicator("config")
protected static class ConfigServerHealthIndicatorConfiguration {
@Bean
public ConfigClientHealthProperties configClientHealthProperties() {
return new ConfigClientHealthProperties();
}
@Bean
public ConfigServerHealthIndicator clientConfigServerHealthIndicator(
ConfigServicePropertySourceLocator locator,
ConfigClientHealthProperties properties, Environment environment) {
return new ConfigServerHealthIndicator(locator, environment, properties);
ConfigClientHealthProperties properties,
ConfigurableEnvironment environment) {
return new ConfigServerHealthIndicator(environment, properties);
}
}

View File

@@ -16,7 +16,11 @@
package org.springframework.cloud.config.client;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
/**
* @author Spencer Gibb
@@ -25,28 +29,16 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class ConfigClientHealthProperties {
/**
* Flag to indicate that the config server health indicator should be installed.
* Time to live for cached result. Default 5 min.
*/
boolean enabled;
@DurationUnit(ChronoUnit.MINUTES)
private Duration timeToLive = Duration.ofMinutes(5);
/**
* Time to live for cached result, in milliseconds. Default 300000 (5 min).
*/
private long timeToLive = 60 * 5 * 1000;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public long getTimeToLive() {
public Duration getTimeToLive() {
return this.timeToLive;
}
public void setTimeToLive(long timeToLive) {
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}

View File

@@ -22,7 +22,8 @@ import java.util.List;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
/**
@@ -31,25 +32,22 @@ import org.springframework.core.env.PropertySource;
*/
public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
private ConfigServicePropertySourceLocator locator;
private ConfigClientHealthProperties properties;
private Environment environment;
private ConfigurableEnvironment environment;
private long lastAccess = 0;
private PropertySource<?> cached;
public ConfigServerHealthIndicator(ConfigServicePropertySourceLocator locator,
Environment environment, ConfigClientHealthProperties properties) {
public ConfigServerHealthIndicator(ConfigurableEnvironment environment,
ConfigClientHealthProperties properties) {
this.environment = environment;
this.locator = locator;
this.properties = properties;
}
@Override
protected void doHealthCheck(Builder builder) throws Exception {
protected void doHealthCheck(Builder builder) {
PropertySource<?> propertySource = getPropertySource();
builder.up();
if (propertySource instanceof CompositePropertySource) {
@@ -72,7 +70,9 @@ public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
long accessTime = System.currentTimeMillis();
if (isCacheStale(accessTime)) {
this.lastAccess = accessTime;
this.cached = this.locator.locate(this.environment);
MutablePropertySources propertySources = this.environment
.getPropertySources();
this.cached = propertySources.get("configClient");
}
return this.cached;
}
@@ -81,7 +81,8 @@ public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
if (this.cached == null) {
return true;
}
return (accessTime - this.lastAccess) >= this.properties.getTimeToLive();
return (accessTime - this.lastAccess) >= this.properties.getTimeToLive()
.toMillis();
}
}

View File

@@ -129,14 +129,17 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
}
}
if (StringUtils.hasText(result.getState())
|| StringUtils.hasText(result.getVersion())) {
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Object> map = new HashMap<>();
if (StringUtils.hasText(result.getState())) {
putValue(map, "config.client.state", result.getState());
putValue(map, "config.client.version", result.getVersion());
composite.addFirstPropertySource(
new MapPropertySource("configClient", map));
}
if (StringUtils.hasText(result.getVersion())) {
putValue(map, "config.client.version", result.getVersion());
}
// the existence of this property source confirms a successful
// response from config server
composite.addFirstPropertySource(
new MapPropertySource("configClient", map));
return composite;
}
}

View File

@@ -18,17 +18,17 @@ package org.springframework.cloud.config.client;
import java.util.Collections;
import org.junit.Test;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.health.Status;
import org.springframework.core.env.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -38,44 +38,44 @@ import static org.mockito.Mockito.verify;
* @author Marcos Barbero
*
*/
@Disabled
public class ConfigServerHealthIndicatorTests {
private ConfigServicePropertySourceLocator locator = mock(
ConfigServicePropertySourceLocator.class);
private Environment environment = mock(Environment.class);
private ConfigurableEnvironment environment = mock(ConfigurableEnvironment.class);
private ConfigServerHealthIndicator indicator = new ConfigServerHealthIndicator(
this.locator, this.environment, new ConfigClientHealthProperties());
this.environment, new ConfigClientHealthProperties());
@Test
public void testDefaultStatus() {
MutablePropertySources sources = new MutablePropertySources();
doReturn(sources).when(this.environment).getPropertySources();
// UNKNOWN is better than DOWN since it doesn't stop the app from working
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
public void testExceptionStatus() {
doThrow(new IllegalStateException()).when(this.locator)
.locate(any(Environment.class));
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.DOWN);
verify(this.locator, times(1)).locate(any(Environment.class));
// TODO: is this needed any more
}
@Test
public void testServerUp() {
PropertySource<?> source = new MapPropertySource("foo",
Collections.<String, Object>emptyMap());
doReturn(source).when(this.locator).locate(any(Environment.class));
setupPropertySources();
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
verify(this.locator, times(1)).locate(any(Environment.class));
}
protected void setupPropertySources() {
PropertySource<?> source = new MapPropertySource("configClient",
Collections.emptyMap());
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(source);
doReturn(sources).when(this.environment).getPropertySources();
}
@Test
public void healthIsCached() {
PropertySource<?> source = new MapPropertySource("foo",
Collections.<String, Object>emptyMap());
doReturn(source).when(this.locator).locate(any(Environment.class));
setupPropertySources();
// not cached
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
@@ -83,7 +83,7 @@ public class ConfigServerHealthIndicatorTests {
// cached
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
verify(this.locator, times(1)).locate(any(Environment.class));
verify(this.environment, times(1)).getPropertySources();
}
}