diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerList.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerList.java index 202c2855..3c400a2c 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerList.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerList.java @@ -22,8 +22,6 @@ import java.util.List; import lombok.Getter; import lombok.Setter; -import org.springframework.util.StringUtils; - import com.netflix.appinfo.InstanceInfo; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; @@ -92,7 +90,7 @@ class DomainExtractingServer extends DiscoveryEnabledServer { setZone(server.getInstanceInfo().getMetadata().get("zone")); } else if (approximateZoneFromHostname) { - setZone(extractApproximateZone(server)); + setZone(ZoneUtils.extractApproximateZone(server.getHost())); } else { setZone(server.getZone()); @@ -112,18 +110,4 @@ class DomainExtractingServer extends DiscoveryEnabledServer { } return super.getId(); } - - private String extractApproximateZone(Server server) { - String host = server.getHost(); - if (!host.contains(".")) { - return host; - } - String[] split = StringUtils.split(host, "."); - StringBuilder builder = new StringBuilder(split[1]); - for (int i = 2; i < split.length; i++) { - builder.append(".").append(split[i]); - } - return builder.toString(); - } - } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfiguration.java index 5c57e4df..daa28d0d 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfiguration.java @@ -21,11 +21,15 @@ import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity import javax.annotation.PostConstruct; +import lombok.extern.apachecommons.CommonsLog; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; import com.netflix.client.config.IClientConfig; import com.netflix.config.ConfigurationManager; @@ -45,8 +49,10 @@ import com.netflix.niws.loadbalancer.NIWSDiscoveryPing; * * @author Spencer Gibb * @author Dave Syer + * @author Ryan Baxter */ @Configuration +@CommonsLog public class EurekaRibbonClientConfiguration { @Value("${ribbon.eureka.approximateZoneFromHostname:false}") @@ -62,13 +68,19 @@ public class EurekaRibbonClientConfiguration { @Autowired(required = false) private EurekaClientConfig clientConfig; + @Autowired + private EurekaInstanceConfigBean eurekaConfig; + public EurekaRibbonClientConfiguration() { } public EurekaRibbonClientConfiguration(EurekaClientConfig clientConfig, - String serviceId) { + String serviceId, EurekaInstanceConfigBean eurekaConfig, + boolean approximateZoneFromHostname) { this.clientConfig = clientConfig; this.serviceId = serviceId; + this.eurekaConfig = eurekaConfig; + this.approximateZoneFromHostname = approximateZoneFromHostname; } @Bean @@ -82,23 +94,36 @@ public class EurekaRibbonClientConfiguration { @Bean @ConditionalOnMissingBean public ServerList ribbonServerList(IClientConfig config) { - DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(config); - DomainExtractingServerList serverList = new DomainExtractingServerList(discoveryServerList, config, this.approximateZoneFromHostname); + DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList( + config); + DomainExtractingServerList serverList = new DomainExtractingServerList( + discoveryServerList, config, this.approximateZoneFromHostname); return serverList; } @PostConstruct public void preprocess() { - if (this.clientConfig != null - && ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone) == null) { - String[] zones = this.clientConfig.getAvailabilityZones(this.clientConfig - .getRegion()); - String zone = zones != null && zones.length > 0 ? zones[0] : null; - if (zone != null) { - // You can set this with archaius.deployment.* (maybe requires - // custom deployment context)? + String zone = ConfigurationManager.getDeploymentContext().getValue( + ContextKey.zone); + if (this.clientConfig != null && StringUtils.isEmpty(zone)) { + if (approximateZoneFromHostname) { + String approxZone = ZoneUtils.extractApproximateZone(eurekaConfig + .getHostname()); + log.debug("Setting Zone To " + approxZone); ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, - zone); + approxZone); + } + else { + String[] zones = this.clientConfig.getAvailabilityZones(this.clientConfig + .getRegion()); + String availabilityZone = zones != null && zones.length > 0 ? zones[0] + : null; + if (availabilityZone != null) { + // You can set this with archaius.deployment.* (maybe requires + // custom deployment context)? + ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, + availabilityZone); + } } } setProp(this.serviceId, DeploymentContextBasedVipAddresses.key(), this.serviceId); diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZoneUtils.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZoneUtils.java new file mode 100644 index 00000000..71ea689b --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZoneUtils.java @@ -0,0 +1,44 @@ +/* Copyright 2013-2015 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.netflix.ribbon.eureka; + +import org.springframework.util.StringUtils; + +/** + * Utility class for dealing with zones. + * @author Ryan Baxter + * + */ +public class ZoneUtils { + + /** + * Approximates Eureka zones from a host name. This method approximates the zone to be + * everything after the first "." in the host name. + * @param host The host name to extract the host name from + * @return The approximate zone + */ + public static String extractApproximateZone(String host) { + if (!host.contains(".")) { + return host; + } + String[] split = StringUtils.split(host, "."); + StringBuilder builder = new StringBuilder(split[1]); + for (int i = 2; i < split.length; i++) { + builder.append(".").append(split[i]); + } + return builder.toString(); + } + +} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfigurationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfigurationTests.java index 6c655a15..bd0cd217 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfigurationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonClientConfigurationTests.java @@ -16,10 +16,17 @@ package org.springframework.cloud.netflix.ribbon.eureka; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration.VALUE_NOT_SET; + import org.junit.After; +import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; +import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import com.netflix.config.ConfigurationManager; @@ -29,15 +36,14 @@ import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.ZoneAwareLoadBalancer; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; -import static org.junit.Assert.*; -import static org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration.VALUE_NOT_SET; - /** * @author Dave Syer + * @author Ryan Baxter */ public class EurekaRibbonClientConfigurationTests { @After + @Before public void close() { ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, ""); } @@ -46,10 +52,11 @@ public class EurekaRibbonClientConfigurationTests { @Ignore public void basicConfigurationCreatedForLoadBalancer() { EurekaClientConfigBean client = new EurekaClientConfigBean(); + EurekaInstanceConfigBean configBean = new EurekaInstanceConfigBean(); client.getAvailabilityZones().put(client.getRegion(), "foo"); SpringClientFactory clientFactory = new SpringClientFactory(); EurekaRibbonClientConfiguration clientPreprocessor = new EurekaRibbonClientConfiguration( - client, "service"); + client, "service", configBean, false); clientPreprocessor.preprocess(); ILoadBalancer balancer = clientFactory.getLoadBalancer("service"); assertNotNull(balancer); @@ -63,8 +70,9 @@ public class EurekaRibbonClientConfigurationTests { @Test public void testSetProp() { EurekaClientConfigBean client = new EurekaClientConfigBean(); + EurekaInstanceConfigBean configBean = new EurekaInstanceConfigBean(); EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration( - client, "myService"); + client, "myService", configBean, false); String serviceId = "myService"; String suffix = "mySuffix"; String value = "myValue"; @@ -76,5 +84,26 @@ public class EurekaRibbonClientConfigurationTests { preprocessor.setProp(serviceId, suffix, value); assertEquals("property has wrong value", value, property.get()); } + + @Test + public void testDefaultZone() { + EurekaClientConfigBean client = new EurekaClientConfigBean(); + EurekaInstanceConfigBean configBean = new EurekaInstanceConfigBean(); + EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration( + client, "myService", configBean, false); + preprocessor.preprocess(); + assertEquals("defaultZone", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone)); + } + + @Test + public void testApproximateZone() { + EurekaClientConfigBean client = new EurekaClientConfigBean(); + EurekaInstanceConfigBean configBean = new EurekaInstanceConfigBean(); + configBean.setHostname("this.is.a.test.com"); + EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration( + client, "myService", configBean, true); + preprocessor.preprocess(); + assertEquals("is.a.test.com", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone)); + } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonClientPreprocessorIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonClientPreprocessorIntegrationTests.java index afdb4c6f..164aa3f9 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonClientPreprocessorIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonClientPreprocessorIntegrationTests.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; +import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; @@ -98,6 +99,12 @@ public class RibbonClientPreprocessorIntegrationTests { filter.setZone("myTestZone"); return filter; } + + @Bean + public EurekaInstanceConfigBean getEurekaInstanceConfigBean() { + EurekaInstanceConfigBean bean = new EurekaInstanceConfigBean(); + return bean; + } } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/ZoneUtilsTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/ZoneUtilsTest.java new file mode 100644 index 00000000..2238ec43 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/ZoneUtilsTest.java @@ -0,0 +1,35 @@ +/* Copyright 2013-2015 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.netflix.ribbon.eureka; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * + * @author Ryan Baxter + * + */ +public class ZoneUtilsTest { + + @Test + public void extractApproximateZoneTest() { + assertTrue("foo".equals(ZoneUtils.extractApproximateZone("foo"))); + assertTrue("bar".equals(ZoneUtils.extractApproximateZone("foo.bar"))); + assertTrue("world.foo.bar".equals(ZoneUtils + .extractApproximateZone("hello.world.foo.bar"))); + } +}