Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/src/main/asciidoc/spring-cloud-commons.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,20 @@ For a Spring Boot Actuator application there are some additional management endp
Patterns such as service discovery, load balancing and circuit breakers lend themselves to a common abstraction layer that can be consumed by all Spring Cloud clients, independent of the implementation (e.g. discovery via Eureka or Consul).


=== @EnableDiscoveryClient

Commons provides the `@EnableDiscoveryClient` annotation. This looks for implementations of the `DiscoveryClient` interface via `META-INF/spring.factories`. Implementations of Discovery Client will add a configuration class to `spring.factories` under the `org.springframework.cloud.client.discovery.EnableDiscoveryClient` key. Examples of `DiscoveryClient` implementations: are http://cloud.spring.io/spring-cloud-netflix/[Spring Cloud Netflix Eureka], http://cloud.spring.io/spring-cloud-consul/[Spring Cloud Consul Discovery] and http://cloud.spring.io/spring-cloud-zookeeper/[Spring Cloud Zookeeper Discovery].

By default, implementations of `DiscoveryClient` will auto-register the local Spring Boot server with the remote discovery server. This can be disabled by setting `autoRegister=false` in `@EnableDiscoveryClient`.

=== ServiceRegistry

Commons now provides a `ServiceRegistry` interface which provides methods like `register(Registration)` and `deregister(Registration)` which allow you to provide custom registered services. `Registration` is a marker interface.

==== Service Registry Actuator Endpoint

A `/service-registry` actuator endpoint is provided by Commons. This endpoint relys on a `Registration` bean in the Spring Application Context. Calling `/service-registry/instance-status` via a GET will return the status of the `Registration`. A POST to the same endpoint with a `String` body will change the status of the current `Registration` to the new value. Please see the documentation of the `ServiceRegistry` implementation you are using for the allowed values for updating the status and the values retured for the status.

=== Spring RestTemplate as a Load Balancer Client

`RestTemplate` can be automatically configured to use ribbon. To create a load balanced `RestTemplate` create a `RestTemplate` `@Bean` and use the `@LoadBalanced` qualifier.
Expand Down
2 changes: 1 addition & 1 deletion spring-cloud-commons-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>1.1.2.RELEASE</version>
<version>1.2.2.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-commons-dependencies</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@
import org.springframework.beans.BeansException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;

/**
* Lifecycle methods that may be useful and common to various DiscoveryClient implementations.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it still various? or is registration (the R type) intended to work only with one sort of client now? I'd probably doc the type param regardless of whether it stays a marker or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R will be specific to the impl (though it won't extend Registration).

*
* @deprecated use {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration} instead. This class will be removed in the next release train.
*
* @author Spencer Gibb
*/
@Deprecated
public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> {

Expand Down Expand Up @@ -89,6 +94,9 @@ public void stop(Runnable callback) {
@Override
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}

Expand All @@ -103,7 +111,7 @@ public void start() {
if (shouldRegisterManagement()) {
registerManagement();
}
this.context .publishEvent(new InstanceRegisteredEvent<>(this,
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
getConfiguration()));
this.running.compareAndSet(false, true);
}
Expand All @@ -113,17 +121,18 @@ public void start() {
protected abstract void setConfiguredPort(int port);

/**
* @return if the management service should be registered with the DiscoveryService
* @return if the management service should be registered with the {@link ServiceRegistry}
*/
protected boolean shouldRegisterManagement() {
return getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
}

/**
* @return the object used to configure the DiscoveryClient
* @return the object used to configure the registration
*/
protected abstract Object getConfiguration();


/**
* Register the local service with the DiscoveryClient

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this doc need to be changed now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, DiscoveryClient was never right, since it wasn't used.

*/
Expand All @@ -147,7 +156,7 @@ protected void deregisterManagement() {
}

/**
* @return if the DiscoveryClient is enabled
* @return true, if the {@link DiscoveryLifecycle} is enabled
*/
protected abstract boolean isEnabled();

Expand Down Expand Up @@ -201,6 +210,10 @@ public boolean isRunning() {
return this.running.get();
}

protected AtomicBoolean getRunning() {
return running;
}

@Override
public int getOrder() {
return this.order;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {

/**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* @author Spencer Gibb
Expand All @@ -28,6 +34,24 @@
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> {

@Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);

AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));

boolean autoRegister = attributes.getBoolean("autoRegister");

if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
}

return imports;
}

@Override
protected boolean isEnabled() {
return new RelaxedPropertyResolver(getEnvironment()).getProperty(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.springframework.cloud.client.serviceregistry;

import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;

/**
* Lifecycle methods that may be useful and common to {@link ServiceRegistry} implementations.
*
* TODO: document the lifecycle
*
* @param <R> registration type passed to the {@link ServiceRegistry}.
*
* @author Spencer Gibb
*/
@SuppressWarnings("deprecation")
public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {

private ServiceRegistry<R> serviceRegistry;

protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}

protected ServiceRegistry<R> getServiceRegistry() {
return this.serviceRegistry;
}

protected abstract R getRegistration();

protected abstract R getManagementRegistration();

/**
* Register the local service with the {@link ServiceRegistry}
*/
@Override
protected void register() {
this.serviceRegistry.register(getRegistration());
}

/**
* Register the local management service with the {@link ServiceRegistry}
*/
@Override
protected void registerManagement() {
this.serviceRegistry.register(getManagementRegistration());
}

/**
* De-register the local service with the {@link ServiceRegistry}
*/
@Override
protected void deregister() {
this.serviceRegistry.deregister(getRegistration());
}

/**
* De-register the local management service with the {@link ServiceRegistry}
*/
@Override
protected void deregisterManagement() {
this.serviceRegistry.deregister(getManagementRegistration());
}

@Override
public void stop() {
if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
this.serviceRegistry.close();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.springframework.cloud.client.serviceregistry;

/**
* @author Spencer Gibb
*/
public interface AutoServiceRegistration {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.springframework.cloud.client.serviceregistry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
* @author Spencer Gibb
*/
@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
public class AutoServiceRegistrationConfiguration {

@Autowired(required = false)
private AutoServiceRegistration autoServiceRegistration;

@Autowired
private AutoServiceRegistrationProperties properties;

@PostConstruct
protected void init() {
if (autoServiceRegistration == null && this.properties.isFailFast()) {
throw new IllegalStateException("Auto Service Registration has been requested, but there is no AutoServiceRegistration bean");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.springframework.cloud.client.serviceregistry;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* @author Spencer Gibb
*/
@ConfigurationProperties("spring.cloud.service-registry.auto-registration")
public class AutoServiceRegistrationProperties {

/** Should startup fail if there is no AutoServiceRegistration, default to false. */
private boolean failFast = false;

public boolean isFailFast() {
return failFast;
}

public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.springframework.cloud.client.serviceregistry;

/**
* @author Spencer Gibb
*/
public interface Registration {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is a marker interface needed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, cause looking at the test I don't really see any benefit of having this Registration interface. Can you explain that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, leftover from a refactor where this interface had a method and I removed it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up putting this back in, because I needed to lookup the registration in the spring context without knowing the implementation.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.springframework.cloud.client.serviceregistry;

/**
* TODO: write javadoc
* @author Spencer Gibb
*/
public interface ServiceRegistry<R extends Registration> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here or somewhere else, we should qualify with an example of what a registration might be

void register(R registration);

void deregister(R registration);

void close();

// TODO: return value for success?
void setStatus(R registration, String status);

// TODO: concrete return value? Interface?
Object getStatus(R registration);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.springframework.cloud.client.serviceregistry;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @author Spencer Gibb
*/
@ConditionalOnBean(ServiceRegistry.class)
@Configuration
public class ServiceRegistryAutoConfiguration {

@Autowired(required = false)
private Registration registration;

@ConditionalOnClass(Endpoint.class)
@Bean
public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) {
ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry);
endpoint.setRegistration(registration);
return endpoint;
}
}
Loading