Merge branch 'rkettelerij-support_multiple_datacenter'

This commit is contained in:
Spencer Gibb
2017-10-23 20:22:42 -04:00
5 changed files with 72 additions and 12 deletions

View File

@@ -121,8 +121,39 @@ spring:
With this metadata, and multiple service instances deployed on localhost, the random value will kick in there to make the instance unique. In Cloudfoundry the `vcap.application.instance_id` will be populated automatically in a Spring Boot application, so the random value will not be needed.
=== Using the DiscoveryClient
Spring Cloud has support for https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign[Feign] (a REST client builder) and also https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-ribbon[Spring `RestTemplate`] using the logical service names instead of physical URLs.
=== Looking up services
==== Using Ribbon
Spring Cloud has support for https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign[Feign] (a REST client builder) and also https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-ribbon[Spring `RestTemplate`]
for looking up services using the logical service names/ids instead of physical URLs. Both Feign and the discovery-aware RestTemplate utilize http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#spring-cloud-ribbon[Ribbon] for client-side load balancing.
If you want to access service STORES using the RestTemplate simply declare:
----
@LoadBalanced
@Bean
public RestTemplate loadbalancedRestTemplate() {
new RestTemplate();
}
----
and use it like this (notice how we use the STORES service name/id from Consul instead of a fully qualified domainname):
----
@Autowired
RestTemplate restTemplate;
public String getFirstProduct() {
return this.restTemplate.getForObject("https://STORES/products/1", String.class);
}
----
If you have Consul clusters in multiple datacenters and you want to access a service in another datacenter a service name/id alone is not enough. In that case
you use property `spring.cloud.consul.discovery.datacenters.STORES=dc-west` where `STORES` is the service name/id and `dc-west` is the datacenter
where the STORES service lives.
==== Using the DiscoveryClient
You can also use the `org.springframework.cloud.client.discovery.DiscoveryClient` which provides a simple API for discovery clients that is not specific to Netflix, e.g.

View File

@@ -37,6 +37,7 @@ import lombok.Setter;
* @author Spencer Gibb
* @author Donnabell Dmello
* @author Venil Noronha
* @author Richard Kettelerij
*/
@ConfigurationProperties("spring.cloud.consul.discovery")
@Data
@@ -132,6 +133,12 @@ public class ConsulDiscoveryProperties {
*/
private Map<String, String> serverListQueryTags = new HashMap<>();
/**
* Map of serviceId's -> datacenter to query for in server list.
* This allows looking up services in another datacenters.
*/
private Map<String, String> datacenters = new HashMap<>();
/** Tag to query for in service list if one is not listed in serverListQueryTags. */
private String defaultQueryTag;
@@ -169,7 +176,6 @@ public class ConsulDiscoveryProperties {
}
/**
*
* @param serviceId The service who's filtering tag is being looked up
* @return The tag the given service id should be filtered by, or null.
*/

View File

@@ -29,6 +29,7 @@ import com.netflix.loadbalancer.AbstractServerList;
/**
* @author Spencer Gibb
* @author Richard Kettelerij
*/
public class ConsulServerList extends AbstractServerList<ConsulServer> {
@@ -103,11 +104,16 @@ public class ConsulServerList extends AbstractServerList<ConsulServer> {
}
/**
* This method will create teh {@link QueryParams} to use when retrieving the
* services from Consul. By default {@link QueryParams#DEFAULT} is used.
* This method will create the {@link QueryParams} to use when retrieving the
* services from Consul. By default {@link QueryParams#DEFAULT} is used. In case
* a datacenter is specified for the current serviceId {@link QueryParams#datacenter} is set.
* @return an instance of {@link QueryParams}
*/
protected QueryParams createQueryParamsForClientRequest() {
String datacenter = getDatacenter();
if (datacenter != null) {
return new QueryParams(datacenter);
}
return QueryParams.DEFAULT;
}
@@ -115,6 +121,10 @@ public class ConsulServerList extends AbstractServerList<ConsulServer> {
return this.properties.getQueryTagForService(this.serviceId);
}
protected String getDatacenter() {
return this.properties.getDatacenters().get(this.serviceId);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ConsulServerList{");

View File

@@ -15,16 +15,19 @@ public class ConsulDiscoveryPropertiesTests {
private static final String DEFAULT_TAG = "defaultTag";
private static final String MAP_TAG = "mapTag";
private static final String MAP_DC = "mapDc";
private static final String SERVICE_NAME_IN_MAP = "serviceNameInMap";
private static final String SERVICE_NAME_NOT_IN_MAP = "serviceNameNotInMap";
private ConsulDiscoveryProperties properties;
private Map<String, String> serverListQueryTags = Collections.singletonMap(SERVICE_NAME_IN_MAP, MAP_TAG);
private Map<String, String> datacenters = Collections.singletonMap(SERVICE_NAME_IN_MAP, MAP_DC);
@Before
public void setUp() throws Exception {
properties = new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties()));
properties.setDefaultQueryTag(DEFAULT_TAG);
properties.setServerListQueryTags(serverListQueryTags);
properties.setDatacenters(datacenters);
}
@Test
@@ -43,4 +46,14 @@ public class ConsulDiscoveryPropertiesTests {
public void testGetTagReturnsMapValueWhenInMap() throws Exception {
assertEquals(MAP_TAG, properties.getQueryTagForService(SERVICE_NAME_IN_MAP));
}
}
@Test
public void testGetDcReturnsNullWhenNotInMap() throws Exception {
assertNull(properties.getDatacenters().get(SERVICE_NAME_NOT_IN_MAP));
}
@Test
public void testGetDcReturnsMapValueWhenInMap() throws Exception {
assertEquals(MAP_DC, properties.getDatacenters().get(SERVICE_NAME_IN_MAP));
}
}

View File

@@ -51,8 +51,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@Deprecated
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestPropsConfig.class,
properties = { "spring.application.name=myTestService-B",
"spring.cloud.consul.discovery.instanceId=myTestService1-B",
properties = { "spring.application.name=myTestService-Z",
"spring.cloud.consul.discovery.instanceId=myTestService1-Z",
"spring.cloud.consul.discovery.port=4452",
"spring.cloud.consul.discovery.hostname=myhost",
"spring.cloud.consul.discovery.ipAddress=10.0.0.1",
@@ -71,16 +71,16 @@ public class ConsulLifecycleCustomizedPropsTests {
public void contextLoads() {
Response<Map<String, Service>> response = consul.getAgentServices();
Map<String, Service> services = response.getValue();
Service service = services.get("myTestService1-B");
Service service = services.get("myTestService1-Z");
assertThat("service was null", service, is(notNullValue()));
assertThat("service port is discovery port", service.getPort(), equalTo(4452));
assertThat("service id was wrong", "myTestService1-B", equalTo(service.getId()));
assertThat("service name was wrong", "myTestService-B", equalTo(service.getService()));
assertThat("service id was wrong", "myTestService1-Z", equalTo(service.getId()));
assertThat("service name was wrong", "myTestService-Z", equalTo(service.getService()));
assertThat("property hostname was wrong", "myhost", equalTo(this.properties.getHostname()));
assertThat("property ipAddress was wrong", "10.0.0.1", equalTo(this.properties.getIpAddress()));
assertThat("service address was wrong", "myhost", equalTo(service.getAddress()));
Response<List<Check>> checkResponse = consul.getHealthChecksForService("myTestService-B", QueryParams.DEFAULT);
Response<List<Check>> checkResponse = consul.getHealthChecksForService("myTestService-Z", QueryParams.DEFAULT);
List<Check> checks = checkResponse.getValue();
assertThat("checks was wrong size", checks, hasSize(0));
}