Cache config client health indicator.

So config server isn't bombarded with requests.
This commit is contained in:
Spencer Gibb
2016-06-24 17:30:01 -06:00
parent 6c0aa53b28
commit b155a1d58b
5 changed files with 118 additions and 27 deletions

View File

@@ -1166,6 +1166,10 @@ If you use another form of security you might need to <<custom-rest-template,pro
`RestTemplate`>> to the `ConfigServicePropertySourceLocator` (e.g. by
grabbing it in the bootstrap context and injecting one).
==== Health Indicator
The Config Client supplies a Spring Boot Health Indicator that attempts to load configuration from Config Server. The health indicator can be disabled by setting `health.config.enabled=false`. The response is also cached for performance reasons. The default cache time to live is 5 minutes. To change that value set the `health.config.time-to-live` property (in milliseconds).
[[custom-rest-template]]
==== Providing A Custom RestTemplate

View File

@@ -53,6 +53,11 @@ public class ConfigClientAutoConfiguration {
return client;
}
@Bean
public ConfigClientHealthProperties configClientHealthProperties() {
return new ConfigClientHealthProperties();
}
@Configuration
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnBean(ConfigServicePropertySourceLocator.class)
@@ -61,24 +66,9 @@ public class ConfigClientAutoConfiguration {
@Bean
public ConfigServerHealthIndicator configServerHealthIndicator(
ConfigServicePropertySourceLocator locator, Environment environment) {
return new ConfigServerHealthIndicator(locator, environment);
}
}
@ConfigurationProperties("health.config")
public static class Health {
/**
* Flag to indicate that the config server health indicator should be installed.
*/
boolean enabled;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
ConfigServicePropertySourceLocator locator,
ConfigClientHealthProperties properties, Environment environment) {
return new ConfigServerHealthIndicator(locator, environment, properties);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2016 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
*
* http://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.cloud.config.client;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Spencer Gibb
*/
@ConfigurationProperties("health.config")
public class ConfigClientHealthProperties {
/**
* Flag to indicate that the config server health indicator should be installed.
*/
boolean enabled;
/**
* 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() {
return timeToLive;
}
public void setTimeToLive(long timeToLive) {
this.timeToLive = timeToLive;
}
}

View File

@@ -16,17 +16,23 @@ import org.springframework.core.env.PropertySource;
public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
private ConfigServicePropertySourceLocator locator;
private ConfigClientHealthProperties properties;
private Environment environment;
private long lastAccess = 0;
private PropertySource<?> cached;
public ConfigServerHealthIndicator(ConfigServicePropertySourceLocator locator,
Environment environment) {
Environment environment, ConfigClientHealthProperties properties) {
this.environment = environment;
this.locator = locator;
this.properties = properties;
}
@Override
protected void doHealthCheck(Builder builder) throws Exception {
PropertySource<?> propertySource = locator.locate(this.environment);
PropertySource<?> propertySource = getPropertySource();
builder.up();
if (propertySource instanceof CompositePropertySource) {
List<String> sources = new ArrayList<>();
@@ -40,4 +46,21 @@ public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
builder.unknown().withDetail("error", "no property sources located");
}
}
private PropertySource<?> getPropertySource() {
long accessTime = System.currentTimeMillis();
if (isCacheStale(accessTime)) {
this.lastAccess = accessTime;
this.cached = locator.locate(this.environment);
}
return this.cached;
}
private boolean isCacheStale(long accessTime) {
if (this.cached == null) {
return true;
}
return (accessTime - this.lastAccess) >= this.properties.getTimeToLive();
}
}

View File

@@ -17,11 +17,16 @@
package org.springframework.cloud.config.client;
import static org.junit.Assert.assertEquals;
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;
import java.util.Collections;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.actuate.health.Status;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
@@ -34,11 +39,11 @@ import org.springframework.core.env.PropertySource;
*/
public class ConfigServerHealthIndicatorTests {
private ConfigServicePropertySourceLocator locator = Mockito
.mock(ConfigServicePropertySourceLocator.class);
private Environment environment = Mockito.mock(Environment.class);
private ConfigServicePropertySourceLocator locator =
mock(ConfigServicePropertySourceLocator.class);
private Environment environment = mock(Environment.class);
private ConfigServerHealthIndicator indicator = new ConfigServerHealthIndicator(
locator, environment);
locator, environment, new ConfigClientHealthProperties());
@Test
public void testDefaultStatus() {
@@ -48,15 +53,32 @@ public class ConfigServerHealthIndicatorTests {
@Test
public void testExceptionStatus() {
Mockito.doThrow(new IllegalStateException()).when(locator).locate(Mockito.any(Environment.class));
doThrow(new IllegalStateException()).when(locator).locate(any(Environment.class));
assertEquals(Status.DOWN, indicator.health().getStatus());
verify(locator, times(1)).locate(any(Environment.class));
}
@Test
public void testServerUp() {
PropertySource<?> source = new MapPropertySource("foo", Collections.<String,Object>emptyMap());
Mockito.doReturn(source).when(locator).locate(Mockito.any(Environment.class));
doReturn(source).when(locator).locate(any(Environment.class));
assertEquals(Status.UP, indicator.health().getStatus());
verify(locator, times(1)).locate(any(Environment.class));
}
@Test
public void healthIsCached() {
PropertySource<?> source = new MapPropertySource("foo", Collections.<String,Object>emptyMap());
doReturn(source).when(locator).locate(any(Environment.class));
// not cached
assertEquals(Status.UP, indicator.health().getStatus());
// cached
assertEquals(Status.UP, indicator.health().getStatus());
verify(locator, times(1)).locate(any(Environment.class));
}
}