diff --git a/spring-cloud-netflix-core/pom.xml b/spring-cloud-netflix-core/pom.xml
index 91e7bb5b..198cb4d6 100644
--- a/spring-cloud-netflix-core/pom.xml
+++ b/spring-cloud-netflix-core/pom.xml
@@ -113,6 +113,10 @@
neo4j-cypher-compiler-2.1
2.1.2
+
+ org.apache.activemq
+ activemq-client
+
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java
index 9a2c5549..a15f6937 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java
@@ -1,15 +1,20 @@
package org.springframework.cloud.netflix.ribbon;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
+import org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
+import com.netflix.loadbalancer.BaseLoadBalancer;
+
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -18,6 +23,12 @@ import java.util.List;
@Configuration
@AutoConfigureAfter(EurekaClientAutoConfiguration.class)
public class RibbonAutoConfiguration {
+
+ @Autowired(required=false)
+ private List balancers = Collections.emptyList();
+
+ @Autowired(required=false)
+ private EurekaRibbonInitializer initializer;
@Bean
@ConditionalOnMissingBean(RestTemplate.class)
@@ -32,7 +43,7 @@ public class RibbonAutoConfiguration {
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
- return new RibbonLoadBalancerClient();
+ return new RibbonLoadBalancerClient(balancers);
}
@Bean
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java
index 7b6542e8..268925c7 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java
@@ -1,57 +1,77 @@
package org.springframework.cloud.netflix.ribbon;
-import com.netflix.client.ClientFactory;
-import com.netflix.loadbalancer.ILoadBalancer;
-import com.netflix.loadbalancer.Server;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import com.netflix.client.ClientFactory;
+import com.netflix.loadbalancer.BaseLoadBalancer;
+import com.netflix.loadbalancer.ILoadBalancer;
+import com.netflix.loadbalancer.Server;
+
/**
* @author Spencer Gibb
+ * @author Dave Syer
*/
public class RibbonLoadBalancerClient implements LoadBalancerClient {
- @Autowired
- private ServerListInitializer serverListInitializer;
- @Override
- public ServiceInstance choose(String serviceId) {
- serverListInitializer.initialize(serviceId);
- ILoadBalancer loadBalancer = ClientFactory.getNamedLoadBalancer(serviceId);
- Server server = loadBalancer.chooseServer(null);
- if (server == null) {
- throw new IllegalStateException("Unable to locate ILoadBalancer for service: "+ serviceId);
- }
- return new RibbonServer(serviceId, server);
- }
+ @Autowired
+ private ServerListInitializer serverListInitializer;
- private class RibbonServer implements ServiceInstance {
- private String serviceId;
- private Server server;
+ private Map balancers = new HashMap();
- private RibbonServer(String serviceId, Server server) {
- this.serviceId = serviceId;
- this.server = server;
- }
+ public RibbonLoadBalancerClient(List balancers) {
+ for (BaseLoadBalancer balancer : balancers) {
+ this.balancers.put(balancer.getName(), balancer);
+ }
+ }
- @Override
- public String getServiceId() {
- return serviceId;
- }
+ @Override
+ public ServiceInstance choose(String serviceId) {
+ serverListInitializer.initialize(serviceId);
+ ILoadBalancer loadBalancer = this.balancers.get(serviceId);
+ if (loadBalancer == null) {
+ loadBalancer = ClientFactory.getNamedLoadBalancer(serviceId);
+ }
+ Server server = loadBalancer.chooseServer("default");
+ if (server == null) {
+ throw new IllegalStateException(
+ "Unable to locate ILoadBalancer for service: " + serviceId);
+ }
+ return new RibbonServer(serviceId, server);
+ }
- @Override
- public String getHost() {
- return server.getHost();
- }
+ private class RibbonServer implements ServiceInstance {
+ private String serviceId;
+ private Server server;
- @Override
- public String getIpAddress() {
- return null; //TODO: ribbon doesn't supply ip
- }
+ private RibbonServer(String serviceId, Server server) {
+ this.serviceId = serviceId;
+ this.server = server;
+ }
- @Override
- public int getPort() {
- return server.getPort();
- }
- }
+ @Override
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ @Override
+ public String getHost() {
+ return server.getHost();
+ }
+
+ @Override
+ public String getIpAddress() {
+ return null; // TODO: ribbon doesn't supply ip
+ }
+
+ @Override
+ public int getPort() {
+ return server.getPort();
+ }
+ }
}
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
new file mode 100644
index 00000000..3685fed8
--- /dev/null
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/DomainExtractingServerList.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013-2014 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 java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import org.springframework.util.StringUtils;
+
+import com.netflix.loadbalancer.Server;
+import com.netflix.loadbalancer.ServerList;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class DomainExtractingServerList implements ServerList {
+
+ private ServerList list;
+
+ public DomainExtractingServerList(ServerList list) {
+ this.list = list;
+ }
+
+ @Override
+ public List getInitialListOfServers() {
+ List servers = setZones(list.getInitialListOfServers());
+ return servers;
+ }
+
+ @Override
+ public List getUpdatedListOfServers() {
+ List servers = setZones(list.getUpdatedListOfServers());
+ return servers;
+ }
+
+ private List setZones(List servers) {
+ for (Server server : servers) {
+ if (!server.getZone().equals("default")) {
+ String zone = extractApproximateZone(server.getId());
+ server.setZone(zone);
+ }
+ }
+ return servers;
+ }
+
+ private String extractApproximateZone(String id) {
+ try {
+ URL url = new URL("http://" + id);
+ String host = url.getHost();
+ if (!host.contains(".")) {
+ return host;
+ }
+ String[] split = StringUtils.split(host, ".");
+ StringBuilder builder = new StringBuilder(split[1]);
+ for (int i=2; i dynamic = (DynamicServerListLoadBalancer) balancer;
+ ServerList list = dynamic.getServerListImpl();
+ if (!(list instanceof DomainExtractingServerList)
+ && !(instance.getDataCenterInfo() instanceof AmazonInfo)) {
+ // This is optional: you can use the native Eureka AWS features by making
+ // the EurekaInstanceConfig.dataCenterInfo an AmazonInfo
+ dynamic.setServerListImpl(new DomainExtractingServerList(list));
+ }
+ }
+ }
+
+ protected void setProp(String serviceId, String suffix, String value) {
+ // how to set the namespace properly?
+ String namespace = "ribbon";
+ ConfigurationManager.getConfigInstance().setProperty(
+ serviceId + "." + namespace + "." + suffix, value);
+ }
- protected void setProp(String serviceId, String suffix, String value) {
- //how to set the namespace properly?
- String namespace = "ribbon";
- ConfigurationManager.getConfigInstance().setProperty(serviceId + "."+ namespace +"." + suffix, value);
- }
}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java
index a924a547..f40f0525 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/RibbonEurekaAutoConfiguration.java
@@ -15,7 +15,10 @@
*/
package org.springframework.cloud.netflix.ribbon.eureka;
+import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
+
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -35,9 +38,12 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnExpression("${ribbon.eureka.enabled:true}")
@AutoConfigureAfter(RibbonAutoConfiguration.class)
public class RibbonEurekaAutoConfiguration {
+
+ @Autowired(required=false)
+ private EurekaInstanceConfig instance;
@Bean
public ServerListInitializer serverListInitializer() {
- return new EurekaRibbonInitializer();
+ return new EurekaRibbonInitializer(instance);
}
}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java
new file mode 100644
index 00000000..9d34d55c
--- /dev/null
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013-2014 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 java.util.ArrayList;
+import java.util.List;
+
+import com.netflix.client.config.IClientConfig;
+import com.netflix.config.ConfigurationManager;
+import com.netflix.config.DeploymentContext.ContextKey;
+import com.netflix.loadbalancer.Server;
+import com.netflix.loadbalancer.ZoneAffinityServerListFilter;
+
+/**
+ * A filter that actively prefers the local zone (as defined by the deployment context, or
+ * the Eureka instance metadata).
+ *
+ * @author Dave Syer
+ *
+ */
+public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter {
+
+ private String zone;
+
+ @Override
+ public void initWithNiwsConfig(IClientConfig niwsClientConfig) {
+ super.initWithNiwsConfig(niwsClientConfig);
+ if (ConfigurationManager.getDeploymentContext() != null) {
+ zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone);
+ }
+ }
+
+ @Override
+ public List getFilteredListOfServers(List servers) {
+ List output = super.getFilteredListOfServers(servers);
+ if (zone != null && output.size() == servers.size()) {
+ List local = new ArrayList();
+ for (Server server : output) {
+ if (zone.equalsIgnoreCase(server.getZone())) {
+ local.add(server);
+ }
+ }
+ if (!local.isEmpty()) {
+ return local;
+ }
+ }
+ return output;
+ }
+
+}
diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonInitializerTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonInitializerTests.java
new file mode 100644
index 00000000..50f88402
--- /dev/null
+++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/EurekaRibbonInitializerTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013-2014 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.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Test;
+import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
+
+import com.netflix.client.ClientFactory;
+import com.netflix.config.ConfigurationManager;
+import com.netflix.config.DeploymentContext.ContextKey;
+import com.netflix.loadbalancer.ILoadBalancer;
+import com.netflix.loadbalancer.Server;
+import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class EurekaRibbonInitializerTests {
+
+ @After
+ public void close() {
+ ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone, "");
+ }
+
+ @Test
+ public void basicConfigurationCreatedForLoadBalancer() {
+ EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean();
+ instance.getMetadataMap().put("zone", "foo");
+ EurekaRibbonInitializer initializer = new EurekaRibbonInitializer(
+ instance);
+ initializer.initialize("service");
+ ILoadBalancer balancer = ClientFactory.getNamedLoadBalancer("service");
+ assertNotNull(balancer);
+ @SuppressWarnings("unchecked")
+ ZoneAwareLoadBalancer aware = (ZoneAwareLoadBalancer) balancer;
+ assertTrue(aware.getServerListImpl() instanceof DomainExtractingServerList);
+ assertEquals("foo", ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone));
+ }
+
+}
diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilterTests.java
new file mode 100644
index 00000000..2ed52384
--- /dev/null
+++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/eureka/ZonePreferenceServerListFilterTests.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013-2014 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.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import com.netflix.loadbalancer.Server;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class ZonePreferenceServerListFilterTests {
+
+ private Server dsyer = new Server("dsyer", 8080);
+ private Server localhost = new Server("localhost", 8080);
+
+ @Before
+ public void init() {
+ dsyer.setZone("dsyer");
+ localhost.setZone("localhost");
+ }
+
+ @Test
+ public void noZoneSet() {
+ ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
+ List result = filter.getFilteredListOfServers(Arrays.asList(localhost));
+ assertEquals(1, result.size());
+ }
+
+ @Test
+ public void withZoneSetAndNoMatches() {
+ ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
+ ReflectionTestUtils.setField(filter, "zone", "dsyer");
+ List result = filter.getFilteredListOfServers(Arrays.asList(localhost));
+ assertEquals(1, result.size());
+ }
+
+ @Test
+ public void withZoneSetAndMatches() {
+ ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
+ ReflectionTestUtils.setField(filter, "zone", "dsyer");
+ List result = filter.getFilteredListOfServers(Arrays.asList(dsyer, localhost));
+ assertEquals(1, result.size());
+ }
+
+}