2. Spring Cloud Commons: Common Abstractions
Prev |  |

# 2. Spring Cloud Commons: Common Abstractions

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 (for example, discovery with Eureka or Consul).

## 2.1 @EnableDiscoveryClient

Spring Cloud Commons provides the `@EnableDiscoveryClient` annotation. This looks for implementations of the `DiscoveryClient` interface with `META-INF/spring.factories`. Implementations of the Discovery Client add a configuration class to `spring.factories` under the `org.springframework.cloud.client.discovery.EnableDiscoveryClient` key. Examples of `DiscoveryClient` implementations include Spring Cloud Netflix Eureka, Spring Cloud Consul Discovery, and Spring Cloud Zookeeper Discovery.

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

| Note
`@EnableDiscoveryClient` is no longer required. You can put a `DiscoveryClient` implementation on the classpath to cause the Spring Boot application to register with the service discovery server.

### 2.1.1 Health Indicator

Commons creates a Spring Boot `HealthIndicator` that `DiscoveryClient` implementations can participate in by implementing `DiscoveryHealthIndicator`. To disable the composite `HealthIndicator`, set `spring.cloud.discovery.client.composite-indicator.enabled=false`. A generic `HealthIndicator` based on `DiscoveryClient` is auto-configured (`DiscoveryClientHealthIndicator`). To disable it, set `spring.cloud.discovery.client.health-indicator.enabled=false`. To disable the description field of the `DiscoveryClientHealthIndicator`, set `spring.cloud.discovery.client.health-indicator.include-description=false`. Otherwise, it can bubble up as the `description` of the rolled up `HealthIndicator`.

### 2.1.2 Ordering `DiscoveryClient` instances

`DiscoveryClient` interface extends `Ordered`. This is useful when using multiple discovery clients, as it allows you to define the order of the returned discovery clients, similar to how you can order the beans loaded by a Spring application. By default, the order of any `DiscoveryClient` is set to `0`. If you want to set a different order for your custom `DiscoveryClient` implementations, you just need to override the `getOrder()` method so that it returns the value that is suitable for your setup. Apart from this, you can use properties to set the order of the `DiscoveryClient` implementations provided by Spring Cloud, among others `ConsulDiscoveryClient`, `EurekaDiscoveryClient` and `ZookeeperDiscoveryClient`. In order to do it, you just need to set the `spring.cloud.{clientIdentifier}.discovery.order` (or `eureka.client.order` for Eureka) property to the desired value.

## 2.2 ServiceRegistry

Commons now provides a `ServiceRegistry` interface that provides methods such as `register(Registration)` and `deregister(Registration)`, which let you provide custom registered services. `Registration` is a marker interface.

The following example shows the `ServiceRegistry` in use:

    @Configuration
    @EnableDiscoveryClient(autoRegister=false)
    public class MyConfiguration {
        private ServiceRegistry registry;

        public MyConfiguration(ServiceRegistry registry) {
            this.registry = registry;
        }

        // called through some external process, such as an event or a custom actuator endpoint
        public void register() {
            Registration registration = constructRegistration();
            this.registry.register(registration);
        }
    }

Each `ServiceRegistry` implementation has its own `Registry` implementation.

- `ZookeeperRegistration` used with `ZookeeperServiceRegistry`
- `EurekaRegistration` used with `EurekaServiceRegistry`
- `ConsulRegistration` used with `ConsulServiceRegistry`

If you are using the `ServiceRegistry` interface, you are going to need to pass the correct `Registry` implementation for the `ServiceRegistry` implementation you are using.

### 2.2.1 ServiceRegistry Auto-Registration

By default, the `ServiceRegistry` implementation auto-registers the running service. To disable that behavior, you can set: * `@EnableDiscoveryClient(autoRegister=false)` to permanently disable auto-registration. * `spring.cloud.service-registry.auto-registration.enabled=false` to disable the behavior through configuration.

#### ServiceRegistry Auto-Registration Events

There are two events that will be fired when a service auto-registers. The first event, called `InstancePreRegisteredEvent`, is fired before the service is registered. The second event, called `InstanceRegisteredEvent`, is fired after the service is registered. You can register an `ApplicationListener`(s) to listen to and react to these events.

| Note
These events will not be fired if `spring.cloud.service-registry.auto-registration.enabled` is set to `false`.

### 2.2.2 Service Registry Actuator Endpoint

Spring Cloud Commons provides a `/service-registry` actuator endpoint. This endpoint relies on a `Registration` bean in the Spring Application Context. Calling `/service-registry` with GET returns the status of the `Registration`. Using POST to the same endpoint with a JSON body changes the status of the current `Registration` to the new value. The JSON body has to include the `status` field with the preferred value. Please see the documentation of the `ServiceRegistry` implementation you use for the allowed values when updating the status and the values returned for the status. For instance, Eureka’s supported statuses are `UP`, `DOWN`, `OUT_OF_SERVICE`, and `UNKNOWN`.

## 2.3 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, as shown in the following example:

    @Configuration
    public class MyConfiguration {

        @LoadBalanced
        @Bean
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }

    public class MyClass {
        @Autowired
        private RestTemplate restTemplate;

        public String doOtherStuff() {
            String results = restTemplate.getForObject("http://stores/stores", String.class);
            return results;
        }
    }

| Caution
A `RestTemplate` bean is no longer created through auto-configuration. Individual applications must create it.

The URI needs to use a virtual host name (that is, a service name, not a host name). The Ribbon client is used to create a full physical address. See RibbonAutoConfiguration for details of how the `RestTemplate` is set up.

## 2.4 Spring WebClient as a Load Balancer Client

`WebClient` can be automatically configured to use the `LoadBalancerClient`. To create a load-balanced `WebClient`, create a `WebClient.Builder` `@Bean` and use the `@LoadBalanced` qualifier, as shown in the following example:

    @Configuration
    public class MyConfiguration {

    	@Bean
    	@LoadBalanced
    	public WebClient.Builder loadBalancedWebClientBuilder() {
    		return WebClient.builder();
    	}
    }

    public class MyClass {
        @Autowired
        private WebClient.Builder webClientBuilder;

        public Mono<String> doOtherStuff() {
            return webClientBuilder.build().get().uri("http://stores/stores")
            				.retrieve().bodyToMono(String.class);
        }
    }

The URI needs to use a virtual host name (that is, a service name, not a host name). The Ribbon client is used to create a full physical address.

### 2.4.1 Retrying Failed Requests

A load-balanced `RestTemplate` can be configured to retry failed requests. By default, this logic is disabled. You can enable it by adding Spring Retry to your application’s classpath. The load-balanced `RestTemplate` honors some of the Ribbon configuration values related to retrying failed requests. You can use `client.ribbon.MaxAutoRetries`, `client.ribbon.MaxAutoRetriesNextServer`, and `client.ribbon.OkToRetryOnAllOperations` properties. If you would like to disable the retry logic with Spring Retry on the classpath, you can set `spring.cloud.loadbalancer.retry.enabled=false`. See the Ribbon documentation for a description of what these properties do.

If you would like to implement a `BackOffPolicy` in your retries, you need to create a bean of type `LoadBalancedRetryFactory` and override the `createBackOffPolicy` method:

    @Configuration
    public class MyConfiguration {
        @Bean
        LoadBalancedRetryFactory retryFactory() {
            return new LoadBalancedRetryFactory() {
                @Override
                public BackOffPolicy createBackOffPolicy(String service) {
            		return new ExponentialBackOffPolicy();
            	}
            };
        }
    }

| Note
`client` in the preceding examples should be replaced with your Ribbon client’s name.

If you want to add one or more `RetryListener` implementations to your retry functionality, you need to create a bean of type `LoadBalancedRetryListenerFactory` and return the `RetryListener` array you would like to use for a given service, as shown in the following example:

    @Configuration
    public class MyConfiguration {
        @Bean
        LoadBalancedRetryListenerFactory retryListenerFactory() {
            return new LoadBalancedRetryListenerFactory() {
                @Override
                public RetryListener[] createRetryListeners(String service) {
                    return new RetryListener[]{new RetryListener() {
                        @Override
                        public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                            //TODO Do you business...
                            return true;
                        }

                        @Override
                         public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                            //TODO Do you business...
                        }

                        @Override
                        public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                            //TODO Do you business...
                        }
                    }};
                }
            };
        }
    }

## 2.5 Multiple RestTemplate objects

If you want a `RestTemplate` that is not load-balanced, create a `RestTemplate` bean and inject it. To access the load-balanced `RestTemplate`, use the `@LoadBalanced` qualifier when you create your `@Bean`, as shown in the following example:\

    @Configuration
    public class MyConfiguration {

        @LoadBalanced
        @Bean
        RestTemplate loadBalanced() {
            return new RestTemplate();
        }

        @Primary
        @Bean
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }

    public class MyClass {
        @Autowired
        private RestTemplate restTemplate;

        @Autowired
        @LoadBalanced
        private RestTemplate loadBalanced;

        public String doOtherStuff() {
            return loadBalanced.getForObject("http://stores/stores", String.class);
        }

        public String doStuff() {
            return restTemplate.getForObject("https://example.com", String.class);
        }
    }

| Important
Notice the use of the `@Primary` annotation on the plain `RestTemplate` declaration in the preceding example to disambiguate the unqualified `@Autowired` injection.

| Tip
If you see errors such as `java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89`, try injecting `RestOperations` or setting `spring.aop.proxyTargetClass=true`.

## 2.6 Spring WebFlux WebClient as a Load Balancer Client

`WebClient` can be configured to use the `LoadBalancerClient`. `LoadBalancerExchangeFilterFunction` is auto-configured if `spring-webflux` is on the classpath. The following example shows how to configure a `WebClient` to use load balancer:

    public class MyClass {
        @Autowired
        private LoadBalancerExchangeFilterFunction lbFunction;

        public Mono<String> doOtherStuff() {
            return WebClient.builder().baseUrl("http://stores")
                .filter(lbFunction)
                .build()
                .get()
                .uri("/stores")
                .retrieve()
                .bodyToMono(String.class);
        }
    }

The URI needs to use a virtual host name (that is, a service name, not a host name). The `LoadBalancerClient` is used to create a full physical address.

## 2.7 Ignore Network Interfaces

Sometimes, it is useful to ignore certain named network interfaces so that they can be excluded from Service Discovery registration (for example, when running in a Docker container). A list of regular expressions can be set to cause the desired network interfaces to be ignored. The following configuration ignores the `docker0` interface and all interfaces that start with `veth`:

application.yml.

    spring:
      cloud:
        inetutils:
          ignoredInterfaces:
            - docker0
            - veth.*

You can also force the use of only specified network addresses by using a list of regular expressions, as shown in the following example:

bootstrap.yml.

    spring:
      cloud:
        inetutils:
          preferredNetworks:
            - 192.168
            - 10.0

You can also force the use of only site-local addresses, as shown in the following example: .application.yml

    spring:
      cloud:
        inetutils:
          useOnlySiteLocalInterfaces: true

See Inet4Address.html.isSiteLocalAddress() for more details about what constitutes a site-local address.

## 2.8 HTTP Client Factories

Spring Cloud Commons provides beans for creating both Apache HTTP clients (`ApacheHttpClientFactory`) and OK HTTP clients (`OkHttpClientFactory`). The `OkHttpClientFactory` bean is created only if the OK HTTP jar is on the classpath. In addition, Spring Cloud Commons provides beans for creating the connection managers used by both clients: `ApacheHttpClientConnectionManagerFactory` for the Apache HTTP client and `OkHttpClientConnectionPoolFactory` for the OK HTTP client. If you would like to customize how the HTTP clients are created in downstream projects, you can provide your own implementation of these beans. In addition, if you provide a bean of type `HttpClientBuilder` or `OkHttpClient.Builder`, the default factories use these builders as the basis for the builders returned to downstream projects. You can also disable the creation of these beans by setting `spring.cloud.httpclientfactories.apache.enabled` or `spring.cloud.httpclientfactories.ok.enabled` to `false`.

## 2.9 Enabled Features

Spring Cloud Commons provides a `/features` actuator endpoint. This endpoint returns features available on the classpath and whether they are enabled. The information returned includes the feature type, name, version, and vendor.

### 2.9.1 Feature types

There are two types of 'features': abstract and named.

Abstract features are features where an interface or abstract class is defined and that an implementation the creates, such as `DiscoveryClient`, `LoadBalancerClient`, or `LockService`. The abstract class or interface is used to find a bean of that type in the context. The version displayed is `bean.getClass().getPackage().getImplementationVersion()`.

Named features are features that do not have a particular class they implement, such as "Circuit Breaker", "API Gateway", "Spring Cloud Bus", and others. These features require a name and a bean type.

### 2.9.2 Declaring features

Any module can declare any number of `HasFeature` beans, as shown in the following examples:

    @Bean
    public HasFeatures commonsFeatures() {
      return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
    }

    @Bean
    public HasFeatures consulFeatures() {
      return HasFeatures.namedFeatures(
        new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
        new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
    }

    @Bean
    HasFeatures localFeatures() {
      return HasFeatures.builder()
          .abstractFeature(Foo.class)
          .namedFeature(new NamedFeature("Bar Feature", Bar.class))
          .abstractFeature(Baz.class)
          .build();
    }

Each of these beans should go in an appropriately guarded `@Configuration`.

## 2.10 Spring Cloud Compatibility Verification

Due to the fact that some users have problem with setting up Spring Cloud application, we’ve decided to add a compatibility verification mechanism. It will break if your current setup is not compatible with Spring Cloud requirements, together with a report, showing what exactly went wrong.

At the moment we verify which version of Spring Boot is added to your classpath.

Example of a report

    ***************************
    APPLICATION FAILED TO START
    ***************************

    Description:

    Your project setup is incompatible with our requirements due to following reasons:

    - Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train

    Action:

    Consider applying the following actions:

    - Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
    You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
    If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section.

In order to disable this feature, set `spring.cloud.compatibility-verifier.enabled` to `false`. If you want to override the compatible Spring Boot versions, just set the `spring.cloud.compatibility-verifier.compatible-boot-versions` property with a comma separated list of compatible Spring Boot versions.

Prev |  |
1. Spring Cloud Context: Application Context Services | Home |