diff --git a/spring-cloud-consul-discovery/pom.xml b/spring-cloud-consul-discovery/pom.xml
index 21cb256a..96295a32 100644
--- a/spring-cloud-consul-discovery/pom.xml
+++ b/spring-cloud-consul-discovery/pom.xml
@@ -133,6 +133,11 @@
spring-boot-starter-web
true
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ true
+
org.springframework.boot
spring-boot-starter-actuator
@@ -147,6 +152,11 @@
spring-cloud-test-support
test
+
+ io.projectreactor
+ reactor-test
+ test
+
diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConditionalOnConsulDiscoveryEnabled.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConditionalOnConsulDiscoveryEnabled.java
new file mode 100644
index 00000000..ffa56614
--- /dev/null
+++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConditionalOnConsulDiscoveryEnabled.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019-2019 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
+ *
+ * https://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.discovery;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+/**
+ * Provides a more succinct conditional
+ * spring.cloud.consul.discovery.enabled.
+ *
+ * @author Tim Ysewyn
+ * @since 2.2.0
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@ConditionalOnProperty(value = "spring.cloud.consul.discovery.enabled",
+ matchIfMissing = true)
+public @interface ConditionalOnConsulDiscoveryEnabled {
+
+}
diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientConfiguration.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientConfiguration.java
index 3bd1a52a..1041426b 100644
--- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientConfiguration.java
+++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryClientConfiguration.java
@@ -18,15 +18,18 @@ package org.springframework.cloud.consul.discovery;
import com.ecwid.consul.v1.ConsulClient;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
+import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.cloud.commons.util.InetUtils;
+import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.consul.ConditionalOnConsulEnabled;
+import org.springframework.cloud.consul.ConsulAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -36,13 +39,14 @@ import org.springframework.context.annotation.Configuration;
* @author Tim Ysewyn
*/
@Configuration
-@ConditionalOnConsulEnabled
-@ConditionalOnProperty(value = "spring.cloud.consul.discovery.enabled",
- matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
+@ConditionalOnBlockingDiscoveryEnabled
+@ConditionalOnConsulEnabled
+@ConditionalOnConsulDiscoveryEnabled
@EnableConfigurationProperties
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class })
+@AutoConfigureAfter({ UtilAutoConfiguration.class, ConsulAutoConfiguration.class })
public class ConsulDiscoveryClientConfiguration {
/**
diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/configclient/ConsulDiscoveryClientConfigServiceBootstrapConfiguration.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/configclient/ConsulDiscoveryClientConfigServiceBootstrapConfiguration.java
index a56b98ec..4fa9fb0c 100644
--- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/configclient/ConsulDiscoveryClientConfigServiceBootstrapConfiguration.java
+++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/configclient/ConsulDiscoveryClientConfigServiceBootstrapConfiguration.java
@@ -22,19 +22,21 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
import org.springframework.cloud.consul.ConsulAutoConfiguration;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfiguration;
+import org.springframework.cloud.consul.discovery.reactive.ConsulReactiveDiscoveryClientConfiguration;
import org.springframework.context.annotation.Configuration;
/**
* Helper for config client that wants to lookup the config server via discovery.
*
* @author Spencer Gibb
+ * @author Tim Ysewyn
*/
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
-@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled",
- matchIfMissing = false)
+@ConditionalOnProperty("spring.cloud.config.discovery.enabled")
@Configuration
@ImportAutoConfiguration({ ConsulAutoConfiguration.class,
- ConsulDiscoveryClientConfiguration.class })
+ ConsulDiscoveryClientConfiguration.class,
+ ConsulReactiveDiscoveryClientConfiguration.class })
public class ConsulDiscoveryClientConfigServiceBootstrapConfiguration {
}
diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java
new file mode 100644
index 00000000..122dbc50
--- /dev/null
+++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/reactive/ConsulReactiveDiscoveryClient.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2019-2019 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
+ *
+ * https://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.discovery.reactive;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.ecwid.consul.v1.ConsulClient;
+import com.ecwid.consul.v1.QueryParams;
+import com.ecwid.consul.v1.Response;
+import com.ecwid.consul.v1.health.model.HealthService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Flux;
+import reactor.core.scheduler.Schedulers;
+
+import org.springframework.cloud.client.DefaultServiceInstance;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
+import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
+import org.springframework.util.StringUtils;
+
+import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost;
+import static org.springframework.cloud.consul.discovery.ConsulServerUtils.getMetadata;
+
+/**
+ * Consul version of {@link ReactiveDiscoveryClient}.
+ *
+ * @author Tim Ysewyn
+ */
+public class ConsulReactiveDiscoveryClient implements ReactiveDiscoveryClient {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(ConsulReactiveDiscoveryClient.class);
+
+ private final ConsulClient client;
+
+ private final ConsulDiscoveryProperties properties;
+
+ public ConsulReactiveDiscoveryClient(ConsulClient client,
+ ConsulDiscoveryProperties properties) {
+ this.client = client;
+ this.properties = properties;
+ }
+
+ @Override
+ public String description() {
+ return "Spring Cloud Consul Reactive Discovery Client";
+ }
+
+ @Override
+ public Flux getInstances(String serviceId) {
+ return Flux.defer(() -> {
+ List instances = new ArrayList<>();
+ for (HealthService healthService : getHealthServices(serviceId)) {
+ instances.add(mapToServiceInstance(healthService, serviceId));
+ }
+ return Flux.fromIterable(instances);
+ }).onErrorResume(exception -> {
+ logger.error("Error getting instances from Consul.", exception);
+ return Flux.empty();
+ }).subscribeOn(Schedulers.boundedElastic());
+ }
+
+ private List getHealthServices(String serviceId) {
+ Response> services = StringUtils
+ .hasText(properties.getAclToken())
+ ? client.getHealthServices(serviceId,
+ properties.getDefaultQueryTag(),
+ properties.isQueryPassing(), QueryParams.DEFAULT,
+ properties.getAclToken())
+ : client.getHealthServices(serviceId,
+ properties.getDefaultQueryTag(),
+ properties.isQueryPassing(), QueryParams.DEFAULT);
+ return services == null ? Collections.emptyList() : services.getValue();
+ }
+
+ private ServiceInstance mapToServiceInstance(HealthService service,
+ String serviceId) {
+ String host = findHost(service);
+ Map metadata = getMetadata(service);
+ boolean secure = false;
+ if (metadata.containsKey("secure")) {
+ secure = Boolean.parseBoolean(metadata.get("secure"));
+ }
+ return new DefaultServiceInstance(service.getService().getId(), serviceId, host,
+ service.getService().getPort(), secure, metadata);
+ }
+
+ @Override
+ public Flux getServices() {
+ return Flux.defer(() -> {
+ Response