From 3bd44d69ca95ef1d1ca510b5208b1108ed95b54b Mon Sep 17 00:00:00 2001 From: spencergibb Date: Wed, 22 Jul 2020 18:20:03 -0400 Subject: [PATCH] Updates to include dns discovery for config server bootstrap. Fixes gh-3827 --- ...ekaConfigServerBootstrapConfiguration.java | 52 ++++++++++++++++--- .../http/RestTemplateEurekaHttpClient.java | 4 ++ ...nfigServerBootstrapConfigurationTests.java | 17 ++++++ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java index 28203aea6..bced10eb3 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java @@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SearchStrategy; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.config.client.ConfigServerInstanceProvider; import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator; @@ -44,7 +45,9 @@ import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient; import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; +import org.springframework.util.StringUtils; /** * Bootstrap configuration for config client that wants to lookup the config server via @@ -56,6 +59,7 @@ import org.springframework.http.HttpStatus; @ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false) @Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties public class EurekaConfigServerBootstrapConfiguration { private static final Log log = LogFactory @@ -75,9 +79,9 @@ public class EurekaConfigServerBootstrapConfiguration { @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true") public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient( - EurekaClientConfigBean config) { + EurekaClientConfigBean config, Environment env) { return (WebClientEurekaHttpClient) new WebClientTransportClientFactory() - .newClient(new DefaultEndpoint(getEurekaUrl(config))); + .newClient(new DefaultEndpoint(getEurekaUrl(config, env))); } @Bean @@ -85,14 +89,15 @@ public class EurekaConfigServerBootstrapConfiguration { @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true, havingValue = "false") public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient( - EurekaClientConfigBean config) { + EurekaClientConfigBean config, Environment env) { return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory() - .newClient(new DefaultEndpoint(getEurekaUrl(config))); + .newClient(new DefaultEndpoint(getEurekaUrl(config, env))); } - private String getEurekaUrl(EurekaClientConfigBean config) { - List urls = EndpointUtils.getServiceUrlsFromConfig(config, - EurekaClientConfigBean.DEFAULT_ZONE, true); + private String getEurekaUrl(EurekaClientConfigBean config, Environment env) { + List urls = EndpointUtils.getDiscoveryServiceUrls(config, + EurekaClientConfigBean.DEFAULT_ZONE, new HostnameBasedUrlRandomizer( + env.getProperty("eureka.instance.hostname"))); return urls.get(0); } @@ -132,4 +137,37 @@ public class EurekaConfigServerBootstrapConfiguration { }; } + private static final class HostnameBasedUrlRandomizer + implements EndpointUtils.ServiceUrlRandomizer { + + private final String hostname; + + private HostnameBasedUrlRandomizer(String hostname) { + this.hostname = hostname; + } + + @Override + public void randomize(List urlList) { + int listSize = 0; + if (urlList != null) { + listSize = urlList.size(); + } + if (!StringUtils.hasText(hostname) || listSize == 0) { + return; + } + // Find the hashcode of the instance hostname and use it to find an entry + // and then arrange the rest of the entries after this entry. + int instanceHashcode = hostname.hashCode(); + if (instanceHashcode < 0) { + instanceHashcode = instanceHashcode * -1; + } + int backupInstance = instanceHashcode % listSize; + for (int i = 0; i < backupInstance; i++) { + String zone = urlList.remove(0); + urlList.add(zone); + } + } + + } + } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java index dceb9d051..cda0dad92 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java @@ -62,6 +62,10 @@ public class RestTemplateEurekaHttpClient implements EurekaHttpClient { } } + public String getServiceUrl() { + return this.serviceUrl; + } + @Override public EurekaHttpResponse register(InstanceInfo info) { String urlPath = serviceUrl + "apps/" + info.getAppName(); diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationTests.java index 25ca695aa..696fec499 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationTests.java @@ -85,6 +85,23 @@ public class EurekaConfigServerBootstrapConfigurationTests { }); } + @Test + public void eurekaDnsConfigurationWorks() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations + .of(EurekaConfigServerBootstrapConfiguration.class)) + .withPropertyValues("spring.cloud.config.discovery.enabled=true", + "eureka.instance.hostname=eurekaclient1", + "eureka.client.use-dns-for-fetching-service-urls=true", + "eureka.client.eureka-server-d-n-s-name=myeurekahost", + "eureka.client.eureka-server-u-r-l-context=eureka", + "eureka.client.eureka-server-port=30000") + .run(context -> { + assertThat(output).contains( + "Cannot get cnames bound to the region:txt.us-east-1.myeurekahost"); + }); + } + @Test public void eurekaConfigServerInstanceProviderCalled() { // FIXME: why do I need to do this? (fails in maven build without it.