diff --git a/docs/src/main/asciidoc/spring-cloud-consul.adoc b/docs/src/main/asciidoc/spring-cloud-consul.adoc
index e8d786d1..7abe180d 100644
--- a/docs/src/main/asciidoc/spring-cloud-consul.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-consul.adoc
@@ -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
diff --git a/spring-cloud-consul-config/pom.xml b/spring-cloud-consul-config/pom.xml
index a66beee2..8b678f04 100644
--- a/spring-cloud-consul-config/pom.xml
+++ b/spring-cloud-consul-config/pom.xml
@@ -22,6 +22,11 @@
spring-boot-starter-web
true
+
+ org.springframework.retry
+ spring-retry
+ true
+
com.ecwid.consul
consul-api
diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySourceLocator.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySourceLocator.java
index 523484a3..93570c55 100644
--- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySourceLocator.java
+++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulPropertySourceLocator.java
@@ -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;
diff --git a/spring-cloud-consul-core/pom.xml b/spring-cloud-consul-core/pom.xml
index 69232ed1..53b2c7a7 100644
--- a/spring-cloud-consul-core/pom.xml
+++ b/spring-cloud-consul-core/pom.xml
@@ -32,6 +32,16 @@
spring-cloud-commons
true
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ true
+
+
+ org.springframework.retry
+ spring-retry
+ true
+
com.ecwid.consul
consul-api
diff --git a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java
index c6b5e414..d65e1b13 100644
--- a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java
+++ b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulAutoConfiguration.java
@@ -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();
+ }
+ }
}
diff --git a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulProperties.java b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulProperties.java
index 3015169b..9e397e77 100644
--- a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulProperties.java
+++ b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/ConsulProperties.java
@@ -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";
diff --git a/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/RetryProperties.java b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/RetryProperties.java
new file mode 100644
index 00000000..91888fb2
--- /dev/null
+++ b/spring-cloud-consul-core/src/main/java/org/springframework/cloud/consul/RetryProperties.java
@@ -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;
+}
diff --git a/spring-cloud-consul-dependencies/pom.xml b/spring-cloud-consul-dependencies/pom.xml
index 8847ba71..e7721ee7 100644
--- a/spring-cloud-consul-dependencies/pom.xml
+++ b/spring-cloud-consul-dependencies/pom.xml
@@ -125,6 +125,11 @@
joda-time
${joda-time.version}
+
+ org.springframework.retry
+ spring-retry
+ 1.1.2.RELEASE
+
org.springframework.cloud
spring-cloud-netflix-dependencies
diff --git a/spring-cloud-consul-discovery/pom.xml b/spring-cloud-consul-discovery/pom.xml
index 7b076717..3b52ad4e 100644
--- a/spring-cloud-consul-discovery/pom.xml
+++ b/spring-cloud-consul-discovery/pom.xml
@@ -82,6 +82,11 @@
ribbon-loadbalancer
true
+
+ org.springframework.retry
+ spring-retry
+ true
+
org.projectlombok
lombok
diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulLifecycle.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulLifecycle.java
index 408ac6d7..8ebc9f61 100644
--- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulLifecycle.java
+++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulLifecycle.java
@@ -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");