diff --git a/docs/src/main/asciidoc/spring-cloud-consul.adoc b/docs/src/main/asciidoc/spring-cloud-consul.adoc index 30832ea1..bc92b699 100644 --- a/docs/src/main/asciidoc/spring-cloud-consul.adoc +++ b/docs/src/main/asciidoc/spring-cloud-consul.adoc @@ -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. diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java index 46ca3917..2aa00a58 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryProperties.java @@ -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 serverListQueryTags = new HashMap<>(); + /** + * Map of serviceId's -> datacenter to query for in server list. + * This allows looking up services in another datacenters. + */ + private Map datacenters = new HashMap<>(); + /** Tag to query for in service list if one is not listed in serverListQueryTags. */ private String defaultQueryTag; @@ -144,6 +151,9 @@ public class ConsulDiscoveryProperties { /** Register as a service in consul. */ private boolean register = true; + /** Disable automatic de-registration of service in consul. */ + private boolean deregister = true; + /** Register health check in consul. Useful during development of a service. */ private boolean registerHealthCheck = true; @@ -169,7 +179,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. */ diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServerList.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServerList.java index 6bf4259c..21653bf7 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServerList.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/discovery/ConsulServerList.java @@ -29,6 +29,7 @@ import com.netflix.loadbalancer.AbstractServerList; /** * @author Spencer Gibb + * @author Richard Kettelerij */ public class ConsulServerList extends AbstractServerList { @@ -103,11 +104,16 @@ public class ConsulServerList extends AbstractServerList { } /** - * 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 { 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{"); diff --git a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistration.java b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistration.java index 8787fa4b..0583f935 100644 --- a/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistration.java +++ b/spring-cloud-consul-discovery/src/main/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistration.java @@ -93,7 +93,7 @@ public class ConsulAutoServiceRegistration extends AbstractAutoServiceRegistrati @Override protected void deregister() { - if (!this.properties.isRegister()) { + if (!this.properties.isRegister() || !this.properties.isDeregister()) { return; } super.deregister(); @@ -101,7 +101,7 @@ public class ConsulAutoServiceRegistration extends AbstractAutoServiceRegistrati @Override protected void deregisterManagement() { - if (!this.properties.isRegister()) { + if (!this.properties.isRegister() || !this.properties.isDeregister()) { return; } super.deregisterManagement(); diff --git a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryPropertiesTests.java b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryPropertiesTests.java index ec3ae170..466b3692 100644 --- a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryPropertiesTests.java +++ b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulDiscoveryPropertiesTests.java @@ -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 serverListQueryTags = Collections.singletonMap(SERVICE_NAME_IN_MAP, MAP_TAG); + private Map 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)); } -} \ No newline at end of file + + @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)); + } +} diff --git a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceDeRegistrationDisabledTests.java b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceDeRegistrationDisabledTests.java new file mode 100644 index 00000000..bc86eecb --- /dev/null +++ b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceDeRegistrationDisabledTests.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2017 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.consul.serviceregistry; + +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.agent.model.Service; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration; +import org.springframework.cloud.consul.ConsulAutoConfiguration; +import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Map; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * @author Jon Freedman + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = ConsulAutoServiceRegistrationDisabledTests.TestConfig.class, + properties = {"spring.application.name=myTestNotDeRegisteredService", + "spring.cloud.consul.discovery.instanceId=myTestNotDeRegisteredService-D", + "spring.cloud.consul.discovery.deregister=false"}, + webEnvironment = RANDOM_PORT) +public class ConsulAutoServiceDeRegistrationDisabledTests { + @Autowired + private ConsulClient consul; + + @Autowired(required = false) + private ConsulAutoServiceRegistration autoServiceRegistration; + + @Autowired(required = false) + private ConsulDiscoveryProperties discoveryProperties; + + @Test + public void contextLoads() { + assertNotNull("ConsulAutoServiceRegistration was not created", autoServiceRegistration); + assertNotNull("ConsulDiscoveryProperties was not created", discoveryProperties); + + checkService(true); + autoServiceRegistration.deregister(); + checkService(true); + discoveryProperties.setDeregister(true); + autoServiceRegistration.deregister(); + checkService(false); + } + + private void checkService(final boolean expected) { + final Response> response = consul.getAgentServices(); + final Map services = response.getValue(); + final Service service = services.get("myTestNotDeRegisteredService-D"); + if (expected) { + assertNotNull("service was not registered", service); + } else { + assertNull("service was registered", service); + } + } + + @Configuration + @EnableAutoConfiguration + @ImportAutoConfiguration({AutoServiceRegistrationConfiguration.class, ConsulAutoConfiguration.class, + ConsulAutoServiceRegistrationAutoConfiguration.class}) + public static class TestConfig { + } +}