Commit 6087bc75 by Dave Syer

Use eureka.instance zone data consistently determining client zone

The zone for a remote server comes from its eureka.instance.metadataMap.zone, but we didn't (until this change) apply the same logic to the client. If the client is registering itslef with eureka then it will have a metadataMap itself and we should be able to just reverse the logic. Fixes gh-991
parent 8952cff2
...@@ -693,13 +693,27 @@ default is a `DomainExtractingServerList` and the purpose of this is ...@@ -693,13 +693,27 @@ default is a `DomainExtractingServerList` and the purpose of this is
to make physical metadata available to the load balancer without using to make physical metadata available to the load balancer without using
AWS AMI metadata (which is what Netflix relies on). By default the AWS AMI metadata (which is what Netflix relies on). By default the
server list will be constructed with "zone" information as provided in server list will be constructed with "zone" information as provided in
the instance metadata (so on the client set the instance metadata (so on the remote clients set
`eureka.instance.metadataMap.zone`), and if that is missing it can use `eureka.instance.metadataMap.zone`), and if that is missing it can use
the domain name from the server hostname as a proxy for zone (if the the domain name from the server hostname as a proxy for zone (if the
flag `approximateZoneFromHostname` is set). Once the zone information is flag `approximateZoneFromHostname` is set). Once the zone information
available it can be used in a `ServerListFilter` (by default it will is available it can be used in a `ServerListFilter`. By default it
be used to locate a server in the same zone as the client because the will be used to locate a server in the same zone as the client because
default is a `ZonePreferenceServerListFilter`). the default is a `ZonePreferenceServerListFilter`. The zone of the
client is determined the same way as the remote instances by default,
i.e. via `eureka.instance.metadataMap.zone`.
NOTE: The orthodox "archaius" way to set the client zone is via a
configuration property called "@zone", and Spring Cloud will use that
in preference to all other settings if it is available (note that the
key will have to be quoted in YAML configuration).
NOTE: If there is no other source of zone data then a guess is made
based on the client configuration (as opposed to the instance
configuration). We take `eureka.client.availabilityZones`, which is a
map from region name to a list of zones, and pull out the first zone
for the instance's own region (i.e. the `eureka.client.region`, which
defaults to "us-east-1" for comatibility with native Netflix).
[[spring-cloud-ribbon-without-eureka]] [[spring-cloud-ribbon-without-eureka]]
=== Example: How to Use Ribbon Without Eureka === Example: How to Use Ribbon Without Eureka
......
...@@ -36,12 +36,12 @@ import com.netflix.loadbalancer.ServerList; ...@@ -36,12 +36,12 @@ import com.netflix.loadbalancer.ServerList;
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList; import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
import com.netflix.niws.loadbalancer.NIWSDiscoveryPing; import com.netflix.niws.loadbalancer.NIWSDiscoveryPing;
import lombok.extern.apachecommons.CommonsLog;
import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses; import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextBasedVipAddresses;
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity; import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty; import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty;
import lombok.extern.apachecommons.CommonsLog;
/** /**
* Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as: * Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
* <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses, * <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
...@@ -104,21 +104,26 @@ public class EurekaRibbonClientConfiguration { ...@@ -104,21 +104,26 @@ public class EurekaRibbonClientConfiguration {
@PostConstruct @PostConstruct
public void preprocess() { public void preprocess() {
String zone = ConfigurationManager.getDeploymentContext().getValue( String zone = ConfigurationManager.getDeploymentContext()
ContextKey.zone); .getValue(ContextKey.zone);
if (this.clientConfig != null && StringUtils.isEmpty(zone)) { if (this.clientConfig != null && StringUtils.isEmpty(zone)) {
if (approximateZoneFromHostname && eurekaConfig != null) { if (this.approximateZoneFromHostname && this.eurekaConfig != null) {
String approxZone = ZoneUtils.extractApproximateZone(eurekaConfig String approxZone = ZoneUtils
.getHostName(false)); .extractApproximateZone(this.eurekaConfig.getHostName(false));
log.debug("Setting Zone To " + approxZone); log.debug("Setting Zone To " + approxZone);
ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,
approxZone); approxZone);
} }
else { else {
String[] zones = this.clientConfig.getAvailabilityZones(this.clientConfig String availabilityZone = this.eurekaConfig == null ? null
.getRegion()); : this.eurekaConfig.getMetadataMap().get("zone");
String availabilityZone = zones != null && zones.length > 0 ? zones[0] if (availabilityZone == null) {
: null; String[] zones = this.clientConfig
.getAvailabilityZones(this.clientConfig.getRegion());
// Pick the first one from the regions we want to connect to
availabilityZone = zones != null && zones.length > 0 ? zones[0]
: null;
}
if (availabilityZone != null) { if (availabilityZone != null) {
// You can set this with archaius.deployment.* (maybe requires // You can set this with archaius.deployment.* (maybe requires
// custom deployment context)? // custom deployment context)?
...@@ -127,7 +132,8 @@ public class EurekaRibbonClientConfiguration { ...@@ -127,7 +132,8 @@ public class EurekaRibbonClientConfiguration {
} }
} }
} }
setRibbonProperty(this.serviceId, DeploymentContextBasedVipAddresses.key(), this.serviceId); setRibbonProperty(this.serviceId, DeploymentContextBasedVipAddresses.key(),
this.serviceId);
setRibbonProperty(this.serviceId, EnableZoneAffinity.key(), "true"); setRibbonProperty(this.serviceId, EnableZoneAffinity.key(), "true");
} }
......
...@@ -36,8 +36,10 @@ import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; ...@@ -36,8 +36,10 @@ import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.VALUE_NOT_SET;
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.*; import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.getProperty;
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.getRibbonKey;
import static org.springframework.cloud.netflix.ribbon.RibbonProperyUtils.setRibbonProperty;
/** /**
* @author Dave Syer * @author Dave Syer
...@@ -83,15 +85,27 @@ public class EurekaRibbonClientConfigurationTests { ...@@ -83,15 +85,27 @@ public class EurekaRibbonClientConfigurationTests {
String serviceId = "myService"; String serviceId = "myService";
String suffix = "mySuffix"; String suffix = "mySuffix";
String value = "myValue"; String value = "myValue";
DynamicStringProperty property = getProperty(getRibbonKey( DynamicStringProperty property = getProperty(getRibbonKey(serviceId, suffix));
serviceId, suffix)); assertEquals("property doesn't have default value", VALUE_NOT_SET,
assertEquals("property doesn't have default value", VALUE_NOT_SET, property.get()); property.get());
setRibbonProperty(serviceId, suffix, value); setRibbonProperty(serviceId, suffix, value);
assertEquals("property has wrong value", value, property.get()); assertEquals("property has wrong value", value, property.get());
setRibbonProperty(serviceId, suffix, value); setRibbonProperty(serviceId, suffix, value);
assertEquals("property has wrong value", value, property.get()); assertEquals("property has wrong value", value, property.get());
} }
@Test
public void testExplicitZone() {
EurekaClientConfigBean client = new EurekaClientConfigBean();
EurekaInstanceConfigBean configBean = getEurekaInstanceConfigBean();
configBean.getMetadataMap().put("zone", "myZone");
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
client, "myService", configBean, false);
preprocessor.preprocess();
assertEquals("myZone",
ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone));
}
@Test @Test
public void testDefaultZone() { public void testDefaultZone() {
EurekaClientConfigBean client = new EurekaClientConfigBean(); EurekaClientConfigBean client = new EurekaClientConfigBean();
...@@ -99,9 +113,10 @@ public class EurekaRibbonClientConfigurationTests { ...@@ -99,9 +113,10 @@ public class EurekaRibbonClientConfigurationTests {
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration( EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
client, "myService", configBean, false); client, "myService", configBean, false);
preprocessor.preprocess(); preprocessor.preprocess();
assertEquals("defaultZone", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone)); assertEquals("defaultZone",
ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone));
} }
@Test @Test
public void testApproximateZone() { public void testApproximateZone() {
EurekaClientConfigBean client = new EurekaClientConfigBean(); EurekaClientConfigBean client = new EurekaClientConfigBean();
...@@ -110,7 +125,8 @@ public class EurekaRibbonClientConfigurationTests { ...@@ -110,7 +125,8 @@ public class EurekaRibbonClientConfigurationTests {
EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration( EurekaRibbonClientConfiguration preprocessor = new EurekaRibbonClientConfiguration(
client, "myService", configBean, true); client, "myService", configBean, true);
preprocessor.preprocess(); preprocessor.preprocess();
assertEquals("is.a.test.com", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone)); assertEquals("is.a.test.com",
ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone));
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment