Use spring-retry for consul config and discovery.

fixes gh-81
This commit is contained in:
Spencer Gibb
2016-02-02 16:32:12 -07:00
parent 6c0264bc89
commit 1c18c220fc
10 changed files with 121 additions and 0 deletions

View File

@@ -185,6 +185,21 @@ You could store a YAML document in any of the keys listed above.
You can change the data key using `spring.cloud.consul.config.data-key`.
[[spring-cloud-consul-retry]]
== Consul Retry
If you expect that the consul agent may occasionally be unavailable when
your app starts, you can ask it to keep trying after a failure. You need to add
`spring-retry` and `spring-boot-starter-aop` to your classpath. The default
behaviour is to retry 6 times with an initial backoff interval of 1000ms and an
exponential multiplier of 1.1 for subsequent backoffs. You can configure these
properties (and others) using `spring.cloud.consul.retry.*` configuration properties.
This works with both Spring Cloud Consul Config and Discovery registration.
TIP: To take full control of the retry add a `@Bean` of type
`RetryOperationsInterceptor` with id "consulRetryInterceptor". Spring
Retry has a `RetryInterceptorBuilder` that makes it easy to create one.
[[spring-cloud-consul-bus]]
== Spring Cloud Bus with Consul

View File

@@ -22,6 +22,11 @@
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.ecwid.consul</groupId>
<artifactId>consul-api</artifactId>

View File

@@ -22,16 +22,19 @@ import java.util.Collections;
import java.util.List;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.retry.annotation.Retryable;
import com.ecwid.consul.v1.ConsulClient;
/**
* @author Spencer Gibb
*/
@Order(0)
public class ConsulPropertySourceLocator implements PropertySourceLocator {
private ConsulClient consul;
@@ -44,6 +47,7 @@ public class ConsulPropertySourceLocator implements PropertySourceLocator {
}
@Override
@Retryable(interceptor = "consulRetryInterceptor")
public PropertySource<?> locate(Environment environment) {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;

View File

@@ -32,6 +32,16 @@
<artifactId>spring-cloud-commons</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.ecwid.consul</groupId>
<artifactId>consul-api</artifactId>

View File

@@ -18,14 +18,21 @@ package org.springframework.cloud.consul;
import com.ecwid.consul.v1.ConsulClient;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
/**
* @author Spencer Gibb
@@ -67,4 +74,23 @@ public class ConsulAutoConfiguration {
return new ConsulHealthIndicator(consulClient);
}
}
@ConditionalOnClass({ Retryable.class, Aspect.class, AopAutoConfiguration.class })
@Configuration
@EnableRetry(proxyTargetClass = true)
@Import(AopAutoConfiguration.class)
@EnableConfigurationProperties(RetryProperties.class)
protected static class RetryConfiguration {
@Bean(name = "consulRetryInterceptor")
@ConditionalOnMissingBean(name = "consulRetryInterceptor")
public RetryOperationsInterceptor consulRetryInterceptor(
RetryProperties properties) {
return RetryInterceptorBuilder
.stateless()
.backOffOptions(properties.getInitialInterval(),
properties.getMultiplier(), properties.getMaxInterval())
.maxAttempts(properties.getMaxAttempts()).build();
}
}
}

View File

@@ -28,12 +28,15 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("spring.cloud.consul")
@Data
public class ConsulProperties {
/** Consul agent hostname. Defaults to 'localhost'. */
@NotNull
private String host = "localhost";
/** Consul agent port. Defaults to '8500'. */
@NotNull
private int port = 8500;
/** Is spring cloud consul enabled */
private boolean enabled = true;
private String prefix = "config";

View File

@@ -0,0 +1,41 @@
/*
* 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.consul;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
/**
* @author Spencer Gibb
*/
@ConfigurationProperties("spring.cloud.consul.retry")
@Data
public class RetryProperties {
/** Initial retry interval in milliseconds. */
private long initialInterval = 1000;
/** Multiplier for next interval. */
private double multiplier = 1.1;
/** Maximum interval for backoff. */
private long maxInterval = 2000;
/** Maximum number of attempts. */
private int maxAttempts = 6;
}

View File

@@ -125,6 +125,11 @@
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-dependencies</artifactId>

View File

@@ -82,6 +82,11 @@
<artifactId>ribbon-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>

View File

@@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;
import org.springframework.retry.annotation.Retryable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -69,6 +70,12 @@ public class ConsulLifecycle extends AbstractDiscoveryLifecycle {
service.setPort(port);
}
@Override
@Retryable(interceptor = "consulRetryInterceptor")
public void start() {
super.start();
}
@Override
protected void register() {
Assert.notNull(service.getPort(), "service.port has not been set");